From 90ed2efc8028c6ba354039282b00363b2e2cb546 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 22 Mar 2024 15:50:36 +0100 Subject: [PATCH 001/212] Change arma::fill to fill:: Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 1 + src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp | 2 +- src/mlpack/methods/ann/ffn_impl.hpp | 2 +- src/mlpack/methods/ann/layer/convolution_impl.hpp | 2 +- src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp | 2 +- src/mlpack/methods/ann/layer/repeat_impl.hpp | 2 +- src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp | 2 +- 7 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 56ca14387d..f68e4191ed 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -20,6 +20,7 @@ namespace mlpack { /* using for armadillo namespace*/ using arma::conv_to; using arma::exp; + using namespace arma::fill; using arma::distr_param; using arma::dot; using arma::join_cols; diff --git a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp index 21abe51a2a..8afed8f71e 100644 --- a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp +++ b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp @@ -132,7 +132,7 @@ class NaiveConvolution // Pad filter and input to the working output shape. InMatType inputPadded(input.n_rows + 2 * paddingRows, - input.n_cols + 2 * paddingCols, arma::fill::zeros); + input.n_cols + 2 * paddingCols, fill::zeros); inputPadded.submat(paddingRows, paddingCols, paddingRows + input.n_rows - 1, paddingCols + input.n_cols - 1) = input; diff --git a/src/mlpack/methods/ann/ffn_impl.hpp b/src/mlpack/methods/ann/ffn_impl.hpp index aaf7545606..404606acbd 100644 --- a/src/mlpack/methods/ann/ffn_impl.hpp +++ b/src/mlpack/methods/ann/ffn_impl.hpp @@ -469,7 +469,7 @@ typename MatType::elem_type FFN< { typename MatType::elem_type res = 0; res += EvaluateWithGradient(parameters, 0, gradient, 1); - MatType tmpGradient(gradient.n_rows, gradient.n_cols, arma::fill::none); + MatType tmpGradient(gradient.n_rows, gradient.n_cols, fill::none); for (size_t i = 1; i < predictors.n_cols; ++i) { res += EvaluateWithGradient(parameters, i, tmpGradient, 1); diff --git a/src/mlpack/methods/ann/layer/convolution_impl.hpp b/src/mlpack/methods/ann/layer/convolution_impl.hpp index 6db085719a..209fdddaa6 100644 --- a/src/mlpack/methods/ann/layer/convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/convolution_impl.hpp @@ -423,7 +423,7 @@ void ConvolutionType< } MatType output(apparentWidth * apparentHeight * inMaps * higherInDimensions, - batchSize, arma::fill::zeros); + batchSize, fill::zeros); CubeType outputCube; MakeAlias(outputCube, output.memptr(), apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); diff --git a/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp b/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp index 96152109cd..a1cc67d18d 100644 --- a/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp @@ -441,7 +441,7 @@ void GroupedConvolutionType< } MatType output(apparentWidth * apparentHeight * inMaps * higherInDimensions, - batchSize, arma::fill::zeros); + batchSize, fill::zeros); CubeType outputCube; MakeAlias(outputCube, output.memptr(), apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); diff --git a/src/mlpack/methods/ann/layer/repeat_impl.hpp b/src/mlpack/methods/ann/layer/repeat_impl.hpp index ea5a83f93c..b4f80bed4b 100644 --- a/src/mlpack/methods/ann/layer/repeat_impl.hpp +++ b/src/mlpack/methods/ann/layer/repeat_impl.hpp @@ -156,7 +156,7 @@ void RepeatType::ComputeOutputDimensions() // element to the input elements. This will be used in the backward // pass with a simple matrix multiplication. backIdxs.set_size(inputSize, sizeMult); - UintCol counts(inputSize, arma::fill::zeros); + UintCol counts(inputSize, fill::zeros); for (size_t i = 0; i < outIdxs.n_elem; i++) { arma::uword r = outIdxs.at(i); diff --git a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp index 5eb93017be..ca9ec533ba 100644 --- a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp +++ b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp @@ -31,7 +31,7 @@ typename MatType::elem_type HingeLossType::Forward( const MatType& target) { MatType temp = target - (target == 0); - MatType temp_zeros(size(target), arma::fill::zeros); + MatType temp_zeros(size(target), fill::zeros); MatType loss = max(temp_zeros, 1 - prediction % temp); From 57adfa035f64d45dba205f1598aa175ddd8dfdc2 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 22 Mar 2024 23:40:38 +0100 Subject: [PATCH 002/212] Add a strucut shim to cover arma::fill namespace Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index f68e4191ed..d7bfd82f73 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -1,6 +1,7 @@ /** * @file core/util/using.hpp * @author Omar Shrit + * @author Ryan Curtin * * This is a set of `using` statements to mitigate any possible risks or * conflicts with local functions. The compiler is supposed to proritise the @@ -20,7 +21,6 @@ namespace mlpack { /* using for armadillo namespace*/ using arma::conv_to; using arma::exp; - using namespace arma::fill; using arma::distr_param; using arma::dot; using arma::join_cols; @@ -72,6 +72,36 @@ namespace mlpack { #endif + namespace fill { + + #ifdef MLPACK_HAS_COOT + struct fill_none : public arma::fill::fill_class, + public coot::fill::fill_class { }; + + struct fill_zeros : public arma::fill::fill_class, + public coot::fill::fill_class { }; + + struct fill_ones : public arma::fill::fill_class, + public coot::fill::fill_class { }; + + struct fill_randu : public arma::fill::fill_class, + public coot::fill::fill_class { }; + + + #else + struct fill_none : public arma::fill::fill_class { }; + struct fill_zeros : public arma::fill::fill_class { }; + struct fill_ones : public arma::fill::fill_class { }; + struct fill_randu : public arma::fill::fill_class { }; + #endif + + static constexpr fill_none none; + static constexpr fill_zeros zeros; + static constexpr fill_ones ones; + static constexpr fill_randu randu; + } // namespace mlpack::fill + + } // namespace mlpack #endif From eb4974bb9bc1068ad26ff4799a1626ec07d932f1 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 22 Mar 2024 23:58:41 +0100 Subject: [PATCH 003/212] Replace imbue with linspace from arma Signed-off-by: Omar Shrit --- src/mlpack/tests/split_data_test.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/mlpack/tests/split_data_test.cpp b/src/mlpack/tests/split_data_test.cpp index 607dc96a89..9bb744ec46 100644 --- a/src/mlpack/tests/split_data_test.cpp +++ b/src/mlpack/tests/split_data_test.cpp @@ -94,8 +94,7 @@ void CheckDuplication(const Row& trainLabels, TEST_CASE("SplitShuffleDataResultMat", "[SplitDataTest]") { mat input(2, 10); - size_t count = 0; // Counter for unique sequential values. - input.imbue([&count] () { return ++count; }); + input.linspace(0, input.n_elems - 1); const auto value = Split(input, 0.2); REQUIRE(std::get<0>(value).n_cols == 8); // Train data. @@ -108,8 +107,7 @@ TEST_CASE("SplitShuffleDataResultMat", "[SplitDataTest]") TEST_CASE("SplitDataResultMat", "[SplitDataTest]") { mat input(2, 10); - size_t count = 0; // Counter for unique sequential values. - input.imbue([&count] () { return ++count; }); + input.linspace(0, input.n_elems - 1); const auto value = Split(input, 0.2, false); REQUIRE(std::get<0>(value).n_cols == 8); // Train data. @@ -123,8 +121,7 @@ TEST_CASE("SplitDataResultMat", "[SplitDataTest]") TEST_CASE("ZeroRatioSplitData", "[SplitDataTest]") { mat input(2, 10); - size_t count = 0; // Counter for unique sequential values. - input.imbue([&count] () { return ++count; }); + input.linspace(0, input.n_elems - 1); const auto value = Split(input, 0, false); REQUIRE(std::get<0>(value).n_cols == 10); // Train data. @@ -138,8 +135,7 @@ TEST_CASE("ZeroRatioSplitData", "[SplitDataTest]") TEST_CASE("TotalRatioSplitData", "[SplitDataTest]") { mat input(2, 10); - size_t count = 0; // Counter for unique sequential values. - input.imbue([&count] () { return ++count; }); + input.linspace(0, input.n_elems - 1); const auto value = Split(input, 1, false); REQUIRE(std::get<0>(value).n_cols == 0); // Train data. @@ -192,9 +188,8 @@ TEST_CASE("SplitCheckSize", "[SplitDataTest]") */ TEST_CASE("SplitDataLargerTest", "[SplitDataTest]") { - size_t count = 0; mat input(10, 497); - input.imbue([&count] () { return ++count; }); + input.linspace(0, input.n_elems - 1); const auto value = Split(input, 0.3); REQUIRE(std::get<0>(value).n_cols == 497 - size_t(0.3 * 497)); @@ -362,10 +357,9 @@ TEST_CASE("SplitDataResultField", "[SplitDataTest]") mat matA(2, 10); mat matB(2, 10); - size_t count = 0; // Counter for unique sequential values. - matA.imbue([&count]() { return ++count; }); - matB.imbue([&count]() { return ++count; }); - + matA.linspace(0, matA.n_elems - 1); + matA.linspace(matA.n_elems, matA.n_elems + matB.n_elems - 1); + input(0, 0) = matA; input(0, 1) = matB; From 370457f4889e1ed7fd668183ae71bba7a8b2adc6 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Thu, 11 Apr 2024 18:16:35 +0200 Subject: [PATCH 004/212] Change arma::fill:: to internal_compat::fill namespace Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 57 ++++++++++++------- .../convolution_rules/naive_convolution.hpp | 2 +- src/mlpack/methods/ann/ffn_impl.hpp | 2 +- .../methods/ann/layer/convolution_impl.hpp | 2 +- .../ann/layer/grouped_convolution_impl.hpp | 2 +- src/mlpack/methods/ann/layer/repeat_impl.hpp | 2 +- .../ann/loss_functions/hinge_loss_impl.hpp | 2 +- .../svdplusplus_method.hpp | 4 +- .../normalization/item_mean_normalization.hpp | 8 +-- .../normalization/user_mean_normalization.hpp | 8 +-- src/mlpack/methods/dbscan/dbscan_impl.hpp | 2 +- .../all_categorical_split_impl.hpp | 8 +-- .../decision_tree/decision_tree_impl.hpp | 2 +- .../decision_tree_regressor_impl.hpp | 2 +- .../methods/decision_tree/gini_gain.hpp | 2 +- .../decision_tree/information_gain.hpp | 2 +- src/mlpack/methods/kde/kde_impl.hpp | 6 +- src/mlpack/methods/kde/kde_rules_impl.hpp | 4 +- .../methods/kmeans/naive_kmeans_impl.hpp | 4 +- src/mlpack/methods/lars/lars_impl.hpp | 6 +- src/mlpack/methods/lmnn/constraints_impl.hpp | 2 +- src/mlpack/methods/lsh/lsh_search_impl.hpp | 2 +- .../environment/cont_double_pole_cart.hpp | 2 +- .../environment/continuous_mountain_car.hpp | 2 +- .../environment/double_pole_cart.hpp | 2 +- .../environment/mountain_car.hpp | 2 +- .../environment/pendulum.hpp | 2 +- .../worker/n_step_q_learning_worker.hpp | 2 +- .../worker/one_step_q_learning_worker.hpp | 2 +- .../worker/one_step_sarsa_worker.hpp | 2 +- .../xgboost/loss_functions/sse_loss.hpp | 2 +- src/mlpack/tests/split_data_test.cpp | 14 ++--- 32 files changed, 88 insertions(+), 75 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index d7bfd82f73..2a9e9fc2e6 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -72,36 +72,49 @@ namespace mlpack { #endif - namespace fill { + namespace internal_compact { - #ifdef MLPACK_HAS_COOT - struct fill_none : public arma::fill::fill_class, - public coot::fill::fill_class { }; + namespace fill { - struct fill_zeros : public arma::fill::fill_class, - public coot::fill::fill_class { }; + #ifdef MLPACK_HAS_COOT + struct fill_none : public arma::fill::fill_class< + arma::fill::fill_none>, + public coot::fill::fill_class< + coot::fill::fill_none> { }; - struct fill_ones : public arma::fill::fill_class, - public coot::fill::fill_class { }; + struct fill_zeros : public arma::fill::fill_class< + arma::fill::fill_zeros>, + public coot::fill::fill_class< + coot::fill::fill_zeros> { }; - struct fill_randu : public arma::fill::fill_class, - public coot::fill::fill_class { }; + struct fill_ones : public arma::fill::fill_class< + arma::fill::fill_ones>, + public coot::fill::fill_class< + coot::fill::fill_ones> { }; + struct fill_randu : public arma::fill::fill_class< + arma::fill::fill_randu>, + public coot::fill::fill_class< + coot::fill::fill_randu> { }; - #else - struct fill_none : public arma::fill::fill_class { }; - struct fill_zeros : public arma::fill::fill_class { }; - struct fill_ones : public arma::fill::fill_class { }; - struct fill_randu : public arma::fill::fill_class { }; - #endif - - static constexpr fill_none none; - static constexpr fill_zeros zeros; - static constexpr fill_ones ones; - static constexpr fill_randu randu; - } // namespace mlpack::fill + #else + struct fill_none : public arma::fill::fill_class< + arma::fill::fill_none> { }; + struct fill_zeros : public arma::fill::fill_class< + arma::fill::fill_zeros> { }; + struct fill_ones : public arma::fill::fill_class< + arma::fill::fill_ones> { }; + struct fill_randu : public arma::fill::fill_class< + arma::fill::fill_randu> { }; + #endif + static constexpr fill_none none; + static constexpr fill_zeros zeros; + static constexpr fill_ones ones; + static constexpr fill_randu randu; + } // namespace mlpack::internal_compact::fill + } // namespace mlpack::internal_compact } // namespace mlpack #endif diff --git a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp index 8afed8f71e..fbe8792a79 100644 --- a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp +++ b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp @@ -132,7 +132,7 @@ class NaiveConvolution // Pad filter and input to the working output shape. InMatType inputPadded(input.n_rows + 2 * paddingRows, - input.n_cols + 2 * paddingCols, fill::zeros); + input.n_cols + 2 * paddingCols, internal_compact::fill::zeros); inputPadded.submat(paddingRows, paddingCols, paddingRows + input.n_rows - 1, paddingCols + input.n_cols - 1) = input; diff --git a/src/mlpack/methods/ann/ffn_impl.hpp b/src/mlpack/methods/ann/ffn_impl.hpp index 220f378d54..e47f565e5e 100644 --- a/src/mlpack/methods/ann/ffn_impl.hpp +++ b/src/mlpack/methods/ann/ffn_impl.hpp @@ -467,7 +467,7 @@ typename MatType::elem_type FFN< { typename MatType::elem_type res = 0; res += EvaluateWithGradient(parameters, 0, gradient, 1); - MatType tmpGradient(gradient.n_rows, gradient.n_cols, fill::none); + MatType tmpGradient(gradient.n_rows, gradient.n_cols, internal_compact::fill::none); for (size_t i = 1; i < predictors.n_cols; ++i) { res += EvaluateWithGradient(parameters, i, tmpGradient, 1); diff --git a/src/mlpack/methods/ann/layer/convolution_impl.hpp b/src/mlpack/methods/ann/layer/convolution_impl.hpp index 209fdddaa6..46be804d23 100644 --- a/src/mlpack/methods/ann/layer/convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/convolution_impl.hpp @@ -423,7 +423,7 @@ void ConvolutionType< } MatType output(apparentWidth * apparentHeight * inMaps * higherInDimensions, - batchSize, fill::zeros); + batchSize, internal_compact::fill::zeros); CubeType outputCube; MakeAlias(outputCube, output.memptr(), apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); diff --git a/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp b/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp index a1cc67d18d..86e6398254 100644 --- a/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp @@ -441,7 +441,7 @@ void GroupedConvolutionType< } MatType output(apparentWidth * apparentHeight * inMaps * higherInDimensions, - batchSize, fill::zeros); + batchSize, internal_compact::fill::zeros); CubeType outputCube; MakeAlias(outputCube, output.memptr(), apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); diff --git a/src/mlpack/methods/ann/layer/repeat_impl.hpp b/src/mlpack/methods/ann/layer/repeat_impl.hpp index b4f80bed4b..ede6015d6f 100644 --- a/src/mlpack/methods/ann/layer/repeat_impl.hpp +++ b/src/mlpack/methods/ann/layer/repeat_impl.hpp @@ -156,7 +156,7 @@ void RepeatType::ComputeOutputDimensions() // element to the input elements. This will be used in the backward // pass with a simple matrix multiplication. backIdxs.set_size(inputSize, sizeMult); - UintCol counts(inputSize, fill::zeros); + UintCol counts(inputSize, internal_compact::fill::zeros); for (size_t i = 0; i < outIdxs.n_elem; i++) { arma::uword r = outIdxs.at(i); diff --git a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp index ca9ec533ba..ee64767069 100644 --- a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp +++ b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp @@ -31,7 +31,7 @@ typename MatType::elem_type HingeLossType::Forward( const MatType& target) { MatType temp = target - (target == 0); - MatType temp_zeros(size(target), fill::zeros); + MatType temp_zeros(size(target), internal_compact::fill::zeros); MatType loss = max(temp_zeros, 1 - prediction % temp); diff --git a/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp b/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp index 28741e1bfc..ebcddafc77 100644 --- a/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp +++ b/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp @@ -96,7 +96,7 @@ class SVDPlusPlusPolicy { // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(h.n_rows, arma::fill::zeros); + arma::vec userVec(h.n_rows, internal_compact::fill::zeros); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; @@ -124,7 +124,7 @@ class SVDPlusPlusPolicy { // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(h.n_rows, arma::fill::zeros); + arma::vec userVec(h.n_rows, internal_compact::fill::zeros); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; diff --git a/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp b/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp index 47a7e1368a..dec6045984 100644 --- a/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp +++ b/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp @@ -49,9 +49,9 @@ class ItemMeanNormalization void Normalize(arma::mat& data) { const size_t itemNum = max(data.row(1)) + 1; - itemMean = arma::vec(itemNum, arma::fill::zeros); + itemMean = arma::vec(itemNum, internal_compact::fill::zeros); // Number of ratings for each item. - arma::Row ratingNum(itemNum, arma::fill::zeros); + arma::Row ratingNum(itemNum, internal_compact::fill::zeros); // Sum ratings for each item. data.each_col([&](arma::vec& datapoint) @@ -89,8 +89,8 @@ class ItemMeanNormalization void Normalize(arma::sp_mat& cleanedData) { // Calculate itemMean. - itemMean = arma::vec(cleanedData.n_rows, arma::fill::zeros); - arma::Col ratingNum(cleanedData.n_rows, arma::fill::zeros); + itemMean = arma::vec(cleanedData.n_rows, internal_compact::fill::zeros); + arma::Col ratingNum(cleanedData.n_rows, internal_compact::fill::zeros); arma::sp_mat::iterator it = cleanedData.begin(); arma::sp_mat::iterator it_end = cleanedData.end(); for (; it != it_end; ++it) diff --git a/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp b/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp index f9ab787bdb..8d0dca7222 100644 --- a/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp +++ b/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp @@ -49,9 +49,9 @@ class UserMeanNormalization void Normalize(arma::mat& data) { const size_t userNum = max(data.row(0)) + 1; - userMean = arma::vec(userNum, arma::fill::zeros); + userMean = arma::vec(userNum, internal_compact::fill::zeros); // Number of ratings for each user. - arma::Row ratingNum(userNum, arma::fill::zeros); + arma::Row ratingNum(userNum, internal_compact::fill::zeros); // Sum ratings for each user. data.each_col([&](arma::vec& datapoint) @@ -89,8 +89,8 @@ class UserMeanNormalization void Normalize(arma::sp_mat& cleanedData) { // Calculate userMean. - userMean = arma::vec(cleanedData.n_cols, arma::fill::zeros); - arma::Col ratingNum(cleanedData.n_cols, arma::fill::zeros); + userMean = arma::vec(cleanedData.n_cols, internal_compact::fill::zeros); + arma::Col ratingNum(cleanedData.n_cols, internal_compact::fill::zeros); arma::sp_mat::iterator it = cleanedData.begin(); arma::sp_mat::iterator it_end = cleanedData.end(); for (; it != it_end; ++it) diff --git a/src/mlpack/methods/dbscan/dbscan_impl.hpp b/src/mlpack/methods/dbscan/dbscan_impl.hpp index e2642de484..d18e3a4dbe 100644 --- a/src/mlpack/methods/dbscan/dbscan_impl.hpp +++ b/src/mlpack/methods/dbscan/dbscan_impl.hpp @@ -112,7 +112,7 @@ size_t DBSCAN::Cluster( // Get a count of all clusters. const size_t numClusters = max(assignments) + 1; - arma::Col counts(numClusters, arma::fill::zeros); + arma::Col counts(numClusters, internal_compact::fill::zeros); for (size_t i = 0; i < assignments.n_elem; ++i) counts[assignments[i]]++; diff --git a/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp b/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp index 3ee94f1818..9130c36739 100644 --- a/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp +++ b/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp @@ -32,7 +32,7 @@ double AllCategoricalSplit::SplitIfBetter( { // Count the number of elements in each potential child. const double epsilon = 1e-7; // Tolerance for floating-point errors. - arma::Col counts(numCategories, arma::fill::zeros); + arma::Col counts(numCategories, internal_compact::fill::zeros); // If we are using weighted training, split the weights for each child too. arma::vec childWeightSums; @@ -58,7 +58,7 @@ double AllCategoricalSplit::SplitIfBetter( // Calculate the gain of the split. First we have to calculate the labels // that would be assigned to each child. - arma::uvec childPositions(numCategories, arma::fill::zeros); + arma::uvec childPositions(numCategories, internal_compact::fill::zeros); std::vector> childLabels(numCategories); std::vector> childWeights(numCategories); @@ -129,7 +129,7 @@ double AllCategoricalSplit::SplitIfBetter( { // Count the number of elements in each potential child. const double epsilon = 1e-7; // Tolerance for floating-point errors. - arma::Col counts(numCategories, arma::fill::zeros); + arma::Col counts(numCategories, internal_compact::fill::zeros); // If we are using weighted training, split the weights for each child too. arma::vec childWeightSums; @@ -155,7 +155,7 @@ double AllCategoricalSplit::SplitIfBetter( // Calculate the gain of the split. First we have to calculate the labels // that would be assigned to each child. - arma::uvec childPositions(numCategories, arma::fill::zeros); + arma::uvec childPositions(numCategories, internal_compact::fill::zeros); std::vector childResponses(numCategories); std::vector childWeights(numCategories); diff --git a/src/mlpack/methods/decision_tree/decision_tree_impl.hpp b/src/mlpack/methods/decision_tree/decision_tree_impl.hpp index b77b897c4a..423052810c 100644 --- a/src/mlpack/methods/decision_tree/decision_tree_impl.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree_impl.hpp @@ -727,7 +727,7 @@ double DecisionTree childCounts(numChildren, arma::fill::zeros); + arma::Row childCounts(numChildren, internal_compact::fill::zeros); for (size_t i = begin; i < begin + count; ++i) childCounts[childAssignments[i - begin]]++; diff --git a/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp b/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp index 210de697ec..adcad40851 100644 --- a/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp @@ -719,7 +719,7 @@ double DecisionTreeRegressor childCounts(numChildren, arma::fill::zeros); + arma::Row childCounts(numChildren, internal_compact::fill::zeros); for (size_t i = begin; i < begin + count; ++i) childCounts[childAssignments[i - begin]]++; diff --git a/src/mlpack/methods/decision_tree/gini_gain.hpp b/src/mlpack/methods/decision_tree/gini_gain.hpp index ea7d4a5407..4cf2c8af11 100644 --- a/src/mlpack/methods/decision_tree/gini_gain.hpp +++ b/src/mlpack/methods/decision_tree/gini_gain.hpp @@ -68,7 +68,7 @@ class GiniGain // Count the number of elements in each class. Use four auxiliary vectors // to exploit SIMD instructions if possible. - arma::vec countSpace(4 * numClasses, arma::fill::zeros); + arma::vec countSpace(4 * numClasses, internal_compact::fill::zeros); arma::vec counts(countSpace.memptr(), numClasses, false, true); arma::vec counts2(countSpace.memptr() + numClasses, numClasses, false, true); diff --git a/src/mlpack/methods/decision_tree/information_gain.hpp b/src/mlpack/methods/decision_tree/information_gain.hpp index 7cf0f1158e..f5b3a16a54 100644 --- a/src/mlpack/methods/decision_tree/information_gain.hpp +++ b/src/mlpack/methods/decision_tree/information_gain.hpp @@ -69,7 +69,7 @@ class InformationGain // Count the number of elements in each class. Use four auxiliary vectors // to exploit SIMD instructions if possible. - arma::vec countSpace(4 * numClasses, arma::fill::zeros); + arma::vec countSpace(4 * numClasses, internal_compact::fill::zeros); arma::vec counts(countSpace.memptr(), numClasses, false, true); arma::vec counts2(countSpace.memptr() + numClasses, numClasses, false, true); diff --git a/src/mlpack/methods/kde/kde_impl.hpp b/src/mlpack/methods/kde/kde_impl.hpp index 9a0e6fe3b1..2683f9607f 100644 --- a/src/mlpack/methods/kde/kde_impl.hpp +++ b/src/mlpack/methods/kde/kde_impl.hpp @@ -388,7 +388,7 @@ Evaluate(MatType querySet, arma::vec& estimations) // Get estimations vector ready. estimations.clear(); estimations.set_size(querySet.n_cols); - estimations.fill(arma::fill::zeros); + estimations.fill(internal_compact::fill::zeros); // Check whether has already been trained. if (!trained) @@ -465,7 +465,7 @@ Evaluate(Tree* queryTree, // Get estimations vector ready. estimations.clear(); estimations.set_size(queryTree->Dataset().n_cols); - estimations.fill(arma::fill::zeros); + estimations.fill(internal_compact::fill::zeros); // Check whether has already been trained. if (!trained) @@ -559,7 +559,7 @@ Evaluate(arma::vec& estimations) // Get estimations vector ready. estimations.clear(); estimations.set_size(referenceTree->Dataset().n_cols); - estimations.fill(arma::fill::zeros); + estimations.fill(internal_compact::fill::zeros); // Clean accumulated alpha if Monte Carlo estimations are available. if (monteCarlo && std::is_same::value) diff --git a/src/mlpack/methods/kde/kde_rules_impl.hpp b/src/mlpack/methods/kde/kde_rules_impl.hpp index 9ecbd43463..29eb24980b 100644 --- a/src/mlpack/methods/kde/kde_rules_impl.hpp +++ b/src/mlpack/methods/kde/kde_rules_impl.hpp @@ -56,11 +56,11 @@ KDERules::KDERules( scores(0) { // Initialize accumError. - accumError = arma::vec(querySet.n_cols, arma::fill::zeros); + accumError = arma::vec(querySet.n_cols, internal_compact::fill::zeros); // Initialize accumMCAlpha only if Monte Carlo estimations are available. if (monteCarlo && kernelIsGaussian) - accumMCAlpha = arma::vec(querySet.n_cols, arma::fill::zeros); + accumMCAlpha = arma::vec(querySet.n_cols, internal_compact::fill::zeros); } //! The base case. diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 9636e2bd9d..e83481ce29 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -44,8 +44,8 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, { // The current state of the K-means is private for each thread arma::mat localCentroids(centroids.n_rows, centroids.n_cols, - arma::fill::zeros); - arma::Col localCounts(centroids.n_cols, arma::fill::zeros); + internal_compact::fill::zeros); + arma::Col localCounts(centroids.n_cols, internal_compact::fill::zeros); #pragma omp for for (size_t i = 0; i < (size_t) dataset.n_cols; ++i) diff --git a/src/mlpack/methods/lars/lars_impl.hpp b/src/mlpack/methods/lars/lars_impl.hpp index 16eaf029b7..eec0e8f738 100644 --- a/src/mlpack/methods/lars/lars_impl.hpp +++ b/src/mlpack/methods/lars/lars_impl.hpp @@ -575,9 +575,9 @@ LARS::Train(const MatType& matX, isIgnored.resize(dataRef.n_cols, false); // Initialize yHat and beta. - arma::Col beta(dataRef.n_cols, arma::fill::zeros); - arma::Col yHat(dataRef.n_rows, arma::fill::zeros); - arma::Col yHatDirection(dataRef.n_rows, arma::fill::none); + arma::Col beta(dataRef.n_cols, internal_compact::fill::zeros); + arma::Col yHat(dataRef.n_rows, internal_compact::fill::zeros); + arma::Col yHatDirection(dataRef.n_rows, internal_compact::fill::none); bool lassocond = false; diff --git a/src/mlpack/methods/lmnn/constraints_impl.hpp b/src/mlpack/methods/lmnn/constraints_impl.hpp index 3469884572..d9b99e8c41 100644 --- a/src/mlpack/methods/lmnn/constraints_impl.hpp +++ b/src/mlpack/methods/lmnn/constraints_impl.hpp @@ -401,7 +401,7 @@ void Constraints::Triplets(arma::Mat& outputMatrix, arma::Mat targetNeighbors(k, dataset.n_cols);; TargetNeighbors(targetNeighbors, dataset, labels, norms); - outputMatrix = arma::Mat(3, k * k * N , arma::fill::zeros); + outputMatrix = arma::Mat(3, k * k * N , internal_compact::fill::zeros); for (size_t i = 0, r = 0; i < N; ++i) { diff --git a/src/mlpack/methods/lsh/lsh_search_impl.hpp b/src/mlpack/methods/lsh/lsh_search_impl.hpp index 3b1edc01d3..7a8e7cf13e 100644 --- a/src/mlpack/methods/lsh/lsh_search_impl.hpp +++ b/src/mlpack/methods/lsh/lsh_search_impl.hpp @@ -299,7 +299,7 @@ void LSHSearch::Train(MatType referenceSet, // 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); + arma::Row secondHashBinCounts(secondHashSize, internal_compact::fill::zeros); for (size_t i = 0; i < secondHashVectors.n_elem; ++i) secondHashBinCounts[secondHashVectors[i]]++; diff --git a/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp b/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp index 7c0120071a..57042fd721 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp @@ -156,7 +156,7 @@ class ContinuousDoublePoleCart // Update the number of steps performed. stepsPerformed++; - arma::vec dydx(6, arma::fill::zeros); + arma::vec dydx(6, internal_compact::fill::zeros); dydx[0] = state.Velocity(); dydx[2] = state.AngularVelocity(1); dydx[4] = state.AngularVelocity(2); diff --git a/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp b/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp index f17e60b640..06938a5425 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp @@ -37,7 +37,7 @@ class ContinuousMountainCar /** * Construct a state instance. */ - State() : data(dimension, arma::fill::zeros) + State() : data(dimension, internal_compact::fill::zeros) { /* Nothing to do here. */ } /** diff --git a/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp b/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp index fc470a56e0..5fbc6d000b 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp @@ -162,7 +162,7 @@ class DoublePoleCart // Update the number of steps performed. stepsPerformed++; - arma::vec dydx(6, arma::fill::zeros); + arma::vec dydx(6, internal_compact::fill::zeros); dydx[0] = state.Velocity(); dydx[2] = state.AngularVelocity(1); dydx[4] = state.AngularVelocity(2); diff --git a/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp b/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp index 3698af6d81..babbb67388 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp @@ -36,7 +36,7 @@ class MountainCar /** * Construct a state instance. */ - State(): data(dimension, arma::fill::zeros) + State(): data(dimension, internal_compact::fill::zeros) { /* Nothing to do here. */ } /** diff --git a/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp b/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp index bf44b6ac6d..b4abb1c72c 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp @@ -39,7 +39,7 @@ class Pendulum /** * Construct a state instance. */ - State() : theta(0), data(dimension, arma::fill::zeros) + State() : theta(0), data(dimension, internal_compact::fill::zeros) { /* Nothing to do here. */ } /** diff --git a/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp b/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp index fa21e97d9e..94e36421b6 100644 --- a/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp +++ b/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp @@ -282,7 +282,7 @@ class NStepQLearningWorker { // Initialize the gradient storage. arma::mat totalGradients(learningNetwork.Parameters().n_rows, - learningNetwork.Parameters().n_cols, arma::fill::zeros); + learningNetwork.Parameters().n_cols, internal_compact::fill::zeros); // Bootstrap from the value of next state. arma::colvec actionValue; diff --git a/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp b/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp index 75e1bed513..2163404ea4 100644 --- a/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp +++ b/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp @@ -282,7 +282,7 @@ class OneStepQLearningWorker { // Initialize the gradient storage. arma::mat totalGradients(learningNetwork.Parameters().n_rows, - learningNetwork.Parameters().n_cols, arma::fill::zeros); + learningNetwork.Parameters().n_cols, internal_compact::fill::zeros); for (size_t i = 0; i < pending.size(); ++i) { TransitionType &transition = pending[i]; diff --git a/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp b/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp index 814b499898..6e115f820a 100644 --- a/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp +++ b/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp @@ -295,7 +295,7 @@ class OneStepSarsaWorker { // Initialize the gradient storage. arma::mat totalGradients(learningNetwork.Parameters().n_rows, - learningNetwork.Parameters().n_cols, arma::fill::zeros); + learningNetwork.Parameters().n_cols, internal_compact::fill::zeros); for (size_t i = 0; i < pending.size(); ++i) { TransitionType &transition = pending[i]; diff --git a/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp b/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp index 1029fa06af..697015d379 100644 --- a/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp +++ b/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp @@ -87,7 +87,7 @@ class SSELoss { // Calculate gradients and hessians. gradients = (input.row(1) - input.row(0)).t(); - hessians = arma::vec(input.n_cols, arma::fill::ones); + hessians = arma::vec(input.n_cols, internal_compact::fill::ones); return std::pow(ApplyL1(accu(gradients)), 2) / (accu(hessians) + lambda); } diff --git a/src/mlpack/tests/split_data_test.cpp b/src/mlpack/tests/split_data_test.cpp index 9bb744ec46..829fd0c7fe 100644 --- a/src/mlpack/tests/split_data_test.cpp +++ b/src/mlpack/tests/split_data_test.cpp @@ -94,7 +94,7 @@ void CheckDuplication(const Row& trainLabels, TEST_CASE("SplitShuffleDataResultMat", "[SplitDataTest]") { mat input(2, 10); - input.linspace(0, input.n_elems - 1); + input = linspace(0, input.n_elem - 1); const auto value = Split(input, 0.2); REQUIRE(std::get<0>(value).n_cols == 8); // Train data. @@ -107,7 +107,7 @@ TEST_CASE("SplitShuffleDataResultMat", "[SplitDataTest]") TEST_CASE("SplitDataResultMat", "[SplitDataTest]") { mat input(2, 10); - input.linspace(0, input.n_elems - 1); + input = linspace(0, input.n_elem - 1); const auto value = Split(input, 0.2, false); REQUIRE(std::get<0>(value).n_cols == 8); // Train data. @@ -121,7 +121,7 @@ TEST_CASE("SplitDataResultMat", "[SplitDataTest]") TEST_CASE("ZeroRatioSplitData", "[SplitDataTest]") { mat input(2, 10); - input.linspace(0, input.n_elems - 1); + input = linspace(0, input.n_elem - 1); const auto value = Split(input, 0, false); REQUIRE(std::get<0>(value).n_cols == 10); // Train data. @@ -135,7 +135,7 @@ TEST_CASE("ZeroRatioSplitData", "[SplitDataTest]") TEST_CASE("TotalRatioSplitData", "[SplitDataTest]") { mat input(2, 10); - input.linspace(0, input.n_elems - 1); + input = linspace(0, input.n_elem - 1); const auto value = Split(input, 1, false); REQUIRE(std::get<0>(value).n_cols == 0); // Train data. @@ -189,7 +189,7 @@ TEST_CASE("SplitCheckSize", "[SplitDataTest]") TEST_CASE("SplitDataLargerTest", "[SplitDataTest]") { mat input(10, 497); - input.linspace(0, input.n_elems - 1); + input = linspace(0, input.n_elem - 1); const auto value = Split(input, 0.3); REQUIRE(std::get<0>(value).n_cols == 497 - size_t(0.3 * 497)); @@ -357,8 +357,8 @@ TEST_CASE("SplitDataResultField", "[SplitDataTest]") mat matA(2, 10); mat matB(2, 10); - matA.linspace(0, matA.n_elems - 1); - matA.linspace(matA.n_elems, matA.n_elems + matB.n_elems - 1); + matA = linspace(0, matA.n_elem - 1); + matA = linspace(matA.n_elem, matA.n_elem + matB.n_elem - 1); input(0, 0) = matA; input(0, 1) = matB; From 88e4a89914e683e9f3b82e6900f674e917be3107 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Thu, 11 Apr 2024 18:29:17 +0200 Subject: [PATCH 005/212] Break the line if it is more than 80 Signed-off-by: Omar Shrit --- src/mlpack/methods/ann/ffn_impl.hpp | 3 ++- src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp | 2 +- .../methods/cf/normalization/item_mean_normalization.hpp | 3 ++- .../methods/cf/normalization/user_mean_normalization.hpp | 3 ++- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 3 ++- src/mlpack/methods/lars/lars_impl.hpp | 3 ++- src/mlpack/methods/lmnn/constraints_impl.hpp | 3 ++- src/mlpack/methods/lsh/lsh_search_impl.hpp | 3 ++- 8 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/mlpack/methods/ann/ffn_impl.hpp b/src/mlpack/methods/ann/ffn_impl.hpp index e47f565e5e..035c78dae1 100644 --- a/src/mlpack/methods/ann/ffn_impl.hpp +++ b/src/mlpack/methods/ann/ffn_impl.hpp @@ -467,7 +467,8 @@ typename MatType::elem_type FFN< { typename MatType::elem_type res = 0; res += EvaluateWithGradient(parameters, 0, gradient, 1); - MatType tmpGradient(gradient.n_rows, gradient.n_cols, internal_compact::fill::none); + MatType tmpGradient(gradient.n_rows, gradient.n_cols, + internal_compact::fill::none); for (size_t i = 1; i < predictors.n_cols; ++i) { res += EvaluateWithGradient(parameters, i, tmpGradient, 1); diff --git a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp index ee64767069..8edd291d06 100644 --- a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp +++ b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp @@ -31,7 +31,7 @@ typename MatType::elem_type HingeLossType::Forward( const MatType& target) { MatType temp = target - (target == 0); - MatType temp_zeros(size(target), internal_compact::fill::zeros); + MatType tempZeros(size(target), internal_compact::fill::zeros); MatType loss = max(temp_zeros, 1 - prediction % temp); diff --git a/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp b/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp index dec6045984..7dd8b8e63e 100644 --- a/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp +++ b/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp @@ -90,7 +90,8 @@ class ItemMeanNormalization { // Calculate itemMean. itemMean = arma::vec(cleanedData.n_rows, internal_compact::fill::zeros); - arma::Col ratingNum(cleanedData.n_rows, internal_compact::fill::zeros); + arma::Col ratingNum(cleanedData.n_rows, + internal_compact::fill::zeros); arma::sp_mat::iterator it = cleanedData.begin(); arma::sp_mat::iterator it_end = cleanedData.end(); for (; it != it_end; ++it) diff --git a/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp b/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp index 8d0dca7222..6e6d61e12e 100644 --- a/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp +++ b/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp @@ -90,7 +90,8 @@ class UserMeanNormalization { // Calculate userMean. userMean = arma::vec(cleanedData.n_cols, internal_compact::fill::zeros); - arma::Col ratingNum(cleanedData.n_cols, internal_compact::fill::zeros); + arma::Col ratingNum(cleanedData.n_cols, + internal_compact::fill::zeros); arma::sp_mat::iterator it = cleanedData.begin(); arma::sp_mat::iterator it_end = cleanedData.end(); for (; it != it_end; ++it) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index e83481ce29..6f27a0d10c 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -45,7 +45,8 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, // The current state of the K-means is private for each thread arma::mat localCentroids(centroids.n_rows, centroids.n_cols, internal_compact::fill::zeros); - arma::Col localCounts(centroids.n_cols, internal_compact::fill::zeros); + arma::Col localCounts(centroids.n_cols, + internal_compact::fill::zeros); #pragma omp for for (size_t i = 0; i < (size_t) dataset.n_cols; ++i) diff --git a/src/mlpack/methods/lars/lars_impl.hpp b/src/mlpack/methods/lars/lars_impl.hpp index eec0e8f738..7501114540 100644 --- a/src/mlpack/methods/lars/lars_impl.hpp +++ b/src/mlpack/methods/lars/lars_impl.hpp @@ -577,7 +577,8 @@ LARS::Train(const MatType& matX, // Initialize yHat and beta. arma::Col beta(dataRef.n_cols, internal_compact::fill::zeros); arma::Col yHat(dataRef.n_rows, internal_compact::fill::zeros); - arma::Col yHatDirection(dataRef.n_rows, internal_compact::fill::none); + arma::Col yHatDirection(dataRef.n_rows, + internal_compact::fill::none); bool lassocond = false; diff --git a/src/mlpack/methods/lmnn/constraints_impl.hpp b/src/mlpack/methods/lmnn/constraints_impl.hpp index d9b99e8c41..d6808fc7bd 100644 --- a/src/mlpack/methods/lmnn/constraints_impl.hpp +++ b/src/mlpack/methods/lmnn/constraints_impl.hpp @@ -401,7 +401,8 @@ void Constraints::Triplets(arma::Mat& outputMatrix, arma::Mat targetNeighbors(k, dataset.n_cols);; TargetNeighbors(targetNeighbors, dataset, labels, norms); - outputMatrix = arma::Mat(3, k * k * N , internal_compact::fill::zeros); + outputMatrix = arma::Mat(3, k * k * N , + internal_compact::fill::zeros); for (size_t i = 0, r = 0; i < N; ++i) { diff --git a/src/mlpack/methods/lsh/lsh_search_impl.hpp b/src/mlpack/methods/lsh/lsh_search_impl.hpp index 7a8e7cf13e..67b4d2a3d7 100644 --- a/src/mlpack/methods/lsh/lsh_search_impl.hpp +++ b/src/mlpack/methods/lsh/lsh_search_impl.hpp @@ -299,7 +299,8 @@ void LSHSearch::Train(MatType referenceSet, // Now, using the hash vectors for each table, count the number of rows we // have in the second hash table. - arma::Row secondHashBinCounts(secondHashSize, internal_compact::fill::zeros); + arma::Row secondHashBinCounts(secondHashSize, + internal_compact::fill::zeros); for (size_t i = 0; i < secondHashVectors.n_elem; ++i) secondHashBinCounts[secondHashVectors[i]]++; From 0b900bbc308878be256422bf1151301da077b225 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 24 Apr 2024 16:10:23 +0200 Subject: [PATCH 006/212] Use @conradsnicta proposal Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 37 ++++++++++++---------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 2a9e9fc2e6..35aa2de636 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -2,6 +2,7 @@ * @file core/util/using.hpp * @author Omar Shrit * @author Ryan Curtin + * @author Conrad Sanderson * * This is a set of `using` statements to mitigate any possible risks or * conflicts with local functions. The compiler is supposed to proritise the @@ -77,35 +78,23 @@ namespace mlpack { namespace fill { #ifdef MLPACK_HAS_COOT - struct fill_none : public arma::fill::fill_class< - arma::fill::fill_none>, - public coot::fill::fill_class< - coot::fill::fill_none> { }; + struct fill_none : public decltype(arma::fill::none), + public decltype(coot::fill::none) { }; - struct fill_zeros : public arma::fill::fill_class< - arma::fill::fill_zeros>, - public coot::fill::fill_class< - coot::fill::fill_zeros> { }; + struct fill_zeros : public decltype(arma::fill::zeros), + public decltype(coot::fill::zeros) { }; - struct fill_ones : public arma::fill::fill_class< - arma::fill::fill_ones>, - public coot::fill::fill_class< - coot::fill::fill_ones> { }; + struct fill_ones : public decltype(arma::fill::ones), + public decltype(coot::fill::ones) { }; - struct fill_randu : public arma::fill::fill_class< - arma::fill::fill_randu>, - public coot::fill::fill_class< - coot::fill::fill_randu> { }; + struct fill_randu : public decltype(arma::fill::randu), + public decltype(coot::fill::randu) { }; #else - struct fill_none : public arma::fill::fill_class< - arma::fill::fill_none> { }; - struct fill_zeros : public arma::fill::fill_class< - arma::fill::fill_zeros> { }; - struct fill_ones : public arma::fill::fill_class< - arma::fill::fill_ones> { }; - struct fill_randu : public arma::fill::fill_class< - arma::fill::fill_randu> { }; + struct fill_none : public decltype(arma::fill::none) { }; + struct fill_zeros : public decltype(arma::fill::zeros) { }; + struct fill_ones : public decltype(arma::fill::ones) { }; + struct fill_randu : public decltype(arma::fill::randu) { }; #endif static constexpr fill_none none; From 364d2145797d5b4491c59918a3d8b779e2bc4261 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 29 May 2024 18:34:36 +0200 Subject: [PATCH 007/212] Initially add conrad proposal Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 71 +++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 0c0e61af27..97e97d1fd2 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -76,38 +76,63 @@ namespace mlpack { using coot::zeros; #endif + +// By default, assume that we are using an Armadillo object. + template + struct GetFillType + { + static constexpr decltype(arma::fill::none) none = arma::fill::none; + static constexpr decltype(arma::fill::zeros) zeros = arma::fill::zeros; + static constexpr decltype(arma::fill::ones) ones = arma::fill::ones; + static constexpr decltype(arma::fill::randu) randu = arma::fill::randu; + static constexpr decltype(arma::fill::randn) randn = arma::fill::randn; + }; - namespace internal_compact { +#ifdef MLPACK_HAS_COOT + // If the matrix type is a Bandicoot type, use Bandicoot fill objects instead. + template::value>::type*> + struct GetFillType + { + static constexpr decltype(coot::fill::none) none = coot::fill::none; + static constexpr decltype(coot::fill::zeros) zeros = coot::fill::zeros; + static constexpr decltype(coot::fill::ones) ones = coot::fill::ones; + static constexpr decltype(coot::fill::randu) randu = coot::fill::randu; + static constexpr decltype(coot::fill::randn) randn = coot::fill::randn; + }; +#endif - namespace fill { + //namespace internal_compat { - #ifdef MLPACK_HAS_COOT - struct fill_none : public decltype(arma::fill::none), - public decltype(coot::fill::none) { }; + //namespace fill { - struct fill_zeros : public decltype(arma::fill::zeros), - public decltype(coot::fill::zeros) { }; + //#ifdef MLPACK_HAS_COOT + //struct fill_none : public decltype(arma::fill::none), + //public decltype(coot::fill::none) { }; - struct fill_ones : public decltype(arma::fill::ones), - public decltype(coot::fill::ones) { }; + //struct fill_zeros : public decltype(arma::fill::zeros), + //public decltype(coot::fill::zeros) { }; - struct fill_randu : public decltype(arma::fill::randu), - public decltype(coot::fill::randu) { }; + //struct fill_ones : public decltype(arma::fill::ones), + //public decltype(coot::fill::ones) { }; - #else - struct fill_none : public decltype(arma::fill::none) { }; - struct fill_zeros : public decltype(arma::fill::zeros) { }; - struct fill_ones : public decltype(arma::fill::ones) { }; - struct fill_randu : public decltype(arma::fill::randu) { }; - #endif + //struct fill_randu : public decltype(arma::fill::randu), + //public decltype(coot::fill::randu) { }; - static constexpr fill_none none; - static constexpr fill_zeros zeros; - static constexpr fill_ones ones; - static constexpr fill_randu randu; + //#else + //struct fill_none : public decltype(arma::fill::none) { }; + //struct fill_zeros : public decltype(arma::fill::zeros) { }; + //struct fill_ones : public decltype(arma::fill::ones) { }; + //struct fill_randu : public decltype(arma::fill::randu) { }; + //#endif - } // namespace mlpack::internal_compact::fill - } // namespace mlpack::internal_compact + //static constexpr fill_none none; + //static constexpr fill_zeros zeros; + //static constexpr fill_ones ones; + //static constexpr fill_randu randu; + + //} // namespace mlpack::GetFillType + //} // namespace mlpack::internal_compat } // namespace mlpack #endif From 083f08539ea399031deca0633d0e4f5bd400f9a7 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 29 May 2024 18:40:33 +0200 Subject: [PATCH 008/212] use arma::fill in places with out templates Signed-off-by: Omar Shrit --- .../methods/ann/convolution_rules/naive_convolution.hpp | 2 +- src/mlpack/methods/ann/ffn_impl.hpp | 2 +- src/mlpack/methods/ann/layer/convolution_impl.hpp | 2 +- src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp | 2 +- src/mlpack/methods/ann/layer/repeat_impl.hpp | 2 +- src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp | 4 ++-- .../cf/decomposition_policies/svdplusplus_method.hpp | 4 ++-- .../methods/cf/normalization/item_mean_normalization.hpp | 8 ++++---- .../methods/cf/normalization/user_mean_normalization.hpp | 8 ++++---- src/mlpack/methods/dbscan/dbscan_impl.hpp | 2 +- .../methods/decision_tree/all_categorical_split_impl.hpp | 8 ++++---- src/mlpack/methods/decision_tree/decision_tree_impl.hpp | 2 +- .../decision_tree/decision_tree_regressor_impl.hpp | 2 +- src/mlpack/methods/decision_tree/gini_gain.hpp | 2 +- src/mlpack/methods/decision_tree/information_gain.hpp | 2 +- src/mlpack/methods/kde/kde_impl.hpp | 6 +++--- src/mlpack/methods/kde/kde_rules_impl.hpp | 4 ++-- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 4 ++-- src/mlpack/methods/lars/lars_impl.hpp | 6 +++--- src/mlpack/methods/lmnn/constraints_impl.hpp | 2 +- src/mlpack/methods/lsh/lsh_search_impl.hpp | 2 +- .../environment/cont_double_pole_cart.hpp | 2 +- .../environment/continuous_mountain_car.hpp | 2 +- .../environment/double_pole_cart.hpp | 2 +- .../reinforcement_learning/environment/mountain_car.hpp | 2 +- .../reinforcement_learning/environment/pendulum.hpp | 2 +- .../worker/n_step_q_learning_worker.hpp | 2 +- .../worker/one_step_q_learning_worker.hpp | 2 +- .../worker/one_step_sarsa_worker.hpp | 2 +- src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp | 2 +- 30 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp index fbe8792a79..21abe51a2a 100644 --- a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp +++ b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp @@ -132,7 +132,7 @@ class NaiveConvolution // Pad filter and input to the working output shape. InMatType inputPadded(input.n_rows + 2 * paddingRows, - input.n_cols + 2 * paddingCols, internal_compact::fill::zeros); + input.n_cols + 2 * paddingCols, arma::fill::zeros); inputPadded.submat(paddingRows, paddingCols, paddingRows + input.n_rows - 1, paddingCols + input.n_cols - 1) = input; diff --git a/src/mlpack/methods/ann/ffn_impl.hpp b/src/mlpack/methods/ann/ffn_impl.hpp index 7a2bbec618..63e468fa81 100644 --- a/src/mlpack/methods/ann/ffn_impl.hpp +++ b/src/mlpack/methods/ann/ffn_impl.hpp @@ -471,7 +471,7 @@ typename MatType::elem_type FFN< typename MatType::elem_type res = 0; res += EvaluateWithGradient(parameters, 0, gradient, 1); MatType tmpGradient(gradient.n_rows, gradient.n_cols, - internal_compact::fill::none); + arma::fill::none); for (size_t i = 1; i < predictors.n_cols; ++i) { res += EvaluateWithGradient(parameters, i, tmpGradient, 1); diff --git a/src/mlpack/methods/ann/layer/convolution_impl.hpp b/src/mlpack/methods/ann/layer/convolution_impl.hpp index 3db0a4b680..c40a703f2c 100644 --- a/src/mlpack/methods/ann/layer/convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/convolution_impl.hpp @@ -422,7 +422,7 @@ void ConvolutionType< } MatType output(apparentWidth * apparentHeight * inMaps * higherInDimensions, - batchSize, internal_compact::fill::zeros); + batchSize, arma::fill::zeros); CubeType outputCube; MakeAlias(outputCube, output, apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); diff --git a/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp b/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp index 61e2d365c5..6c60e9b128 100644 --- a/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp @@ -440,7 +440,7 @@ void GroupedConvolutionType< } MatType output(apparentWidth * apparentHeight * inMaps * higherInDimensions, - batchSize, internal_compact::fill::zeros); + batchSize, arma::fill::zeros); CubeType outputCube; MakeAlias(outputCube, output, apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); diff --git a/src/mlpack/methods/ann/layer/repeat_impl.hpp b/src/mlpack/methods/ann/layer/repeat_impl.hpp index ede6015d6f..ea5a83f93c 100644 --- a/src/mlpack/methods/ann/layer/repeat_impl.hpp +++ b/src/mlpack/methods/ann/layer/repeat_impl.hpp @@ -156,7 +156,7 @@ void RepeatType::ComputeOutputDimensions() // element to the input elements. This will be used in the backward // pass with a simple matrix multiplication. backIdxs.set_size(inputSize, sizeMult); - UintCol counts(inputSize, internal_compact::fill::zeros); + UintCol counts(inputSize, arma::fill::zeros); for (size_t i = 0; i < outIdxs.n_elem; i++) { arma::uword r = outIdxs.at(i); diff --git a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp index 8edd291d06..5b24b371f2 100644 --- a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp +++ b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp @@ -31,9 +31,9 @@ typename MatType::elem_type HingeLossType::Forward( const MatType& target) { MatType temp = target - (target == 0); - MatType tempZeros(size(target), internal_compact::fill::zeros); + MatType tempZeros(size(target), arma::fill::zeros); - MatType loss = max(temp_zeros, 1 - prediction % temp); + MatType loss = max(tempZeros, 1 - prediction % temp); typename MatType::elem_type lossSum = accu(loss); diff --git a/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp b/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp index ebcddafc77..28741e1bfc 100644 --- a/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp +++ b/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp @@ -96,7 +96,7 @@ class SVDPlusPlusPolicy { // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(h.n_rows, internal_compact::fill::zeros); + arma::vec userVec(h.n_rows, arma::fill::zeros); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; @@ -124,7 +124,7 @@ class SVDPlusPlusPolicy { // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(h.n_rows, internal_compact::fill::zeros); + arma::vec userVec(h.n_rows, arma::fill::zeros); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; diff --git a/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp b/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp index 7dd8b8e63e..251574a879 100644 --- a/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp +++ b/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp @@ -49,9 +49,9 @@ class ItemMeanNormalization void Normalize(arma::mat& data) { const size_t itemNum = max(data.row(1)) + 1; - itemMean = arma::vec(itemNum, internal_compact::fill::zeros); + itemMean = arma::vec(itemNum, arma::fill::zeros); // Number of ratings for each item. - arma::Row ratingNum(itemNum, internal_compact::fill::zeros); + arma::Row ratingNum(itemNum, arma::fill::zeros); // Sum ratings for each item. data.each_col([&](arma::vec& datapoint) @@ -89,9 +89,9 @@ class ItemMeanNormalization void Normalize(arma::sp_mat& cleanedData) { // Calculate itemMean. - itemMean = arma::vec(cleanedData.n_rows, internal_compact::fill::zeros); + itemMean = arma::vec(cleanedData.n_rows, arma::fill::zeros); arma::Col ratingNum(cleanedData.n_rows, - internal_compact::fill::zeros); + arma::fill::zeros); arma::sp_mat::iterator it = cleanedData.begin(); arma::sp_mat::iterator it_end = cleanedData.end(); for (; it != it_end; ++it) diff --git a/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp b/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp index 6e6d61e12e..7b991adde5 100644 --- a/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp +++ b/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp @@ -49,9 +49,9 @@ class UserMeanNormalization void Normalize(arma::mat& data) { const size_t userNum = max(data.row(0)) + 1; - userMean = arma::vec(userNum, internal_compact::fill::zeros); + userMean = arma::vec(userNum, arma::fill::zeros); // Number of ratings for each user. - arma::Row ratingNum(userNum, internal_compact::fill::zeros); + arma::Row ratingNum(userNum, arma::fill::zeros); // Sum ratings for each user. data.each_col([&](arma::vec& datapoint) @@ -89,9 +89,9 @@ class UserMeanNormalization void Normalize(arma::sp_mat& cleanedData) { // Calculate userMean. - userMean = arma::vec(cleanedData.n_cols, internal_compact::fill::zeros); + userMean = arma::vec(cleanedData.n_cols, arma::fill::zeros); arma::Col ratingNum(cleanedData.n_cols, - internal_compact::fill::zeros); + arma::fill::zeros); arma::sp_mat::iterator it = cleanedData.begin(); arma::sp_mat::iterator it_end = cleanedData.end(); for (; it != it_end; ++it) diff --git a/src/mlpack/methods/dbscan/dbscan_impl.hpp b/src/mlpack/methods/dbscan/dbscan_impl.hpp index d18e3a4dbe..e2642de484 100644 --- a/src/mlpack/methods/dbscan/dbscan_impl.hpp +++ b/src/mlpack/methods/dbscan/dbscan_impl.hpp @@ -112,7 +112,7 @@ size_t DBSCAN::Cluster( // Get a count of all clusters. const size_t numClusters = max(assignments) + 1; - arma::Col counts(numClusters, internal_compact::fill::zeros); + arma::Col counts(numClusters, arma::fill::zeros); for (size_t i = 0; i < assignments.n_elem; ++i) counts[assignments[i]]++; diff --git a/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp b/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp index 9130c36739..3ee94f1818 100644 --- a/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp +++ b/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp @@ -32,7 +32,7 @@ double AllCategoricalSplit::SplitIfBetter( { // Count the number of elements in each potential child. const double epsilon = 1e-7; // Tolerance for floating-point errors. - arma::Col counts(numCategories, internal_compact::fill::zeros); + arma::Col counts(numCategories, arma::fill::zeros); // If we are using weighted training, split the weights for each child too. arma::vec childWeightSums; @@ -58,7 +58,7 @@ double AllCategoricalSplit::SplitIfBetter( // Calculate the gain of the split. First we have to calculate the labels // that would be assigned to each child. - arma::uvec childPositions(numCategories, internal_compact::fill::zeros); + arma::uvec childPositions(numCategories, arma::fill::zeros); std::vector> childLabels(numCategories); std::vector> childWeights(numCategories); @@ -129,7 +129,7 @@ double AllCategoricalSplit::SplitIfBetter( { // Count the number of elements in each potential child. const double epsilon = 1e-7; // Tolerance for floating-point errors. - arma::Col counts(numCategories, internal_compact::fill::zeros); + arma::Col counts(numCategories, arma::fill::zeros); // If we are using weighted training, split the weights for each child too. arma::vec childWeightSums; @@ -155,7 +155,7 @@ double AllCategoricalSplit::SplitIfBetter( // Calculate the gain of the split. First we have to calculate the labels // that would be assigned to each child. - arma::uvec childPositions(numCategories, internal_compact::fill::zeros); + arma::uvec childPositions(numCategories, arma::fill::zeros); std::vector childResponses(numCategories); std::vector childWeights(numCategories); diff --git a/src/mlpack/methods/decision_tree/decision_tree_impl.hpp b/src/mlpack/methods/decision_tree/decision_tree_impl.hpp index 423052810c..b77b897c4a 100644 --- a/src/mlpack/methods/decision_tree/decision_tree_impl.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree_impl.hpp @@ -727,7 +727,7 @@ double DecisionTree childCounts(numChildren, internal_compact::fill::zeros); + arma::Row childCounts(numChildren, arma::fill::zeros); for (size_t i = begin; i < begin + count; ++i) childCounts[childAssignments[i - begin]]++; diff --git a/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp b/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp index adcad40851..210de697ec 100644 --- a/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp @@ -719,7 +719,7 @@ double DecisionTreeRegressor childCounts(numChildren, internal_compact::fill::zeros); + arma::Row childCounts(numChildren, arma::fill::zeros); for (size_t i = begin; i < begin + count; ++i) childCounts[childAssignments[i - begin]]++; diff --git a/src/mlpack/methods/decision_tree/gini_gain.hpp b/src/mlpack/methods/decision_tree/gini_gain.hpp index 4cf2c8af11..ea7d4a5407 100644 --- a/src/mlpack/methods/decision_tree/gini_gain.hpp +++ b/src/mlpack/methods/decision_tree/gini_gain.hpp @@ -68,7 +68,7 @@ class GiniGain // Count the number of elements in each class. Use four auxiliary vectors // to exploit SIMD instructions if possible. - arma::vec countSpace(4 * numClasses, internal_compact::fill::zeros); + arma::vec countSpace(4 * numClasses, arma::fill::zeros); arma::vec counts(countSpace.memptr(), numClasses, false, true); arma::vec counts2(countSpace.memptr() + numClasses, numClasses, false, true); diff --git a/src/mlpack/methods/decision_tree/information_gain.hpp b/src/mlpack/methods/decision_tree/information_gain.hpp index f5b3a16a54..7cf0f1158e 100644 --- a/src/mlpack/methods/decision_tree/information_gain.hpp +++ b/src/mlpack/methods/decision_tree/information_gain.hpp @@ -69,7 +69,7 @@ class InformationGain // Count the number of elements in each class. Use four auxiliary vectors // to exploit SIMD instructions if possible. - arma::vec countSpace(4 * numClasses, internal_compact::fill::zeros); + arma::vec countSpace(4 * numClasses, arma::fill::zeros); arma::vec counts(countSpace.memptr(), numClasses, false, true); arma::vec counts2(countSpace.memptr() + numClasses, numClasses, false, true); diff --git a/src/mlpack/methods/kde/kde_impl.hpp b/src/mlpack/methods/kde/kde_impl.hpp index 2683f9607f..9a0e6fe3b1 100644 --- a/src/mlpack/methods/kde/kde_impl.hpp +++ b/src/mlpack/methods/kde/kde_impl.hpp @@ -388,7 +388,7 @@ Evaluate(MatType querySet, arma::vec& estimations) // Get estimations vector ready. estimations.clear(); estimations.set_size(querySet.n_cols); - estimations.fill(internal_compact::fill::zeros); + estimations.fill(arma::fill::zeros); // Check whether has already been trained. if (!trained) @@ -465,7 +465,7 @@ Evaluate(Tree* queryTree, // Get estimations vector ready. estimations.clear(); estimations.set_size(queryTree->Dataset().n_cols); - estimations.fill(internal_compact::fill::zeros); + estimations.fill(arma::fill::zeros); // Check whether has already been trained. if (!trained) @@ -559,7 +559,7 @@ Evaluate(arma::vec& estimations) // Get estimations vector ready. estimations.clear(); estimations.set_size(referenceTree->Dataset().n_cols); - estimations.fill(internal_compact::fill::zeros); + estimations.fill(arma::fill::zeros); // Clean accumulated alpha if Monte Carlo estimations are available. if (monteCarlo && std::is_same::value) diff --git a/src/mlpack/methods/kde/kde_rules_impl.hpp b/src/mlpack/methods/kde/kde_rules_impl.hpp index 5296921201..83b725708c 100644 --- a/src/mlpack/methods/kde/kde_rules_impl.hpp +++ b/src/mlpack/methods/kde/kde_rules_impl.hpp @@ -56,11 +56,11 @@ KDERules::KDERules( scores(0) { // Initialize accumError. - accumError = arma::vec(querySet.n_cols, internal_compact::fill::zeros); + accumError = arma::vec(querySet.n_cols, arma::fill::zeros); // Initialize accumMCAlpha only if Monte Carlo estimations are available. if (monteCarlo && kernelIsGaussian) - accumMCAlpha = arma::vec(querySet.n_cols, internal_compact::fill::zeros); + accumMCAlpha = arma::vec(querySet.n_cols, arma::fill::zeros); } //! The base case. diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 6f27a0d10c..4f9b8a316c 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -44,9 +44,9 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, { // The current state of the K-means is private for each thread arma::mat localCentroids(centroids.n_rows, centroids.n_cols, - internal_compact::fill::zeros); + arma::fill::zeros); arma::Col localCounts(centroids.n_cols, - internal_compact::fill::zeros); + arma::fill::zeros); #pragma omp for for (size_t i = 0; i < (size_t) dataset.n_cols; ++i) diff --git a/src/mlpack/methods/lars/lars_impl.hpp b/src/mlpack/methods/lars/lars_impl.hpp index 20215a13d9..7234c977f2 100644 --- a/src/mlpack/methods/lars/lars_impl.hpp +++ b/src/mlpack/methods/lars/lars_impl.hpp @@ -575,10 +575,10 @@ LARS::Train(const MatType& matX, isIgnored.resize(dataRef.n_cols, false); // Initialize yHat and beta. - arma::Col beta(dataRef.n_cols, internal_compact::fill::zeros); - arma::Col yHat(dataRef.n_rows, internal_compact::fill::zeros); + arma::Col beta(dataRef.n_cols, arma::fill::zeros); + arma::Col yHat(dataRef.n_rows, arma::fill::zeros); arma::Col yHatDirection(dataRef.n_rows, - internal_compact::fill::none); + arma::fill::none); bool lassocond = false; diff --git a/src/mlpack/methods/lmnn/constraints_impl.hpp b/src/mlpack/methods/lmnn/constraints_impl.hpp index d6808fc7bd..00ef349a89 100644 --- a/src/mlpack/methods/lmnn/constraints_impl.hpp +++ b/src/mlpack/methods/lmnn/constraints_impl.hpp @@ -402,7 +402,7 @@ void Constraints::Triplets(arma::Mat& outputMatrix, TargetNeighbors(targetNeighbors, dataset, labels, norms); outputMatrix = arma::Mat(3, k * k * N , - internal_compact::fill::zeros); + arma::fill::zeros); for (size_t i = 0, r = 0; i < N; ++i) { diff --git a/src/mlpack/methods/lsh/lsh_search_impl.hpp b/src/mlpack/methods/lsh/lsh_search_impl.hpp index 6b414970a4..9d9c3be8a2 100644 --- a/src/mlpack/methods/lsh/lsh_search_impl.hpp +++ b/src/mlpack/methods/lsh/lsh_search_impl.hpp @@ -300,7 +300,7 @@ void LSHSearch::Train(MatType referenceSet, // Now, using the hash vectors for each table, count the number of rows we // have in the second hash table. arma::Row secondHashBinCounts(secondHashSize, - internal_compact::fill::zeros); + arma::fill::zeros); for (size_t i = 0; i < secondHashVectors.n_elem; ++i) secondHashBinCounts[secondHashVectors[i]]++; diff --git a/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp b/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp index 57042fd721..7c0120071a 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp @@ -156,7 +156,7 @@ class ContinuousDoublePoleCart // Update the number of steps performed. stepsPerformed++; - arma::vec dydx(6, internal_compact::fill::zeros); + arma::vec dydx(6, arma::fill::zeros); dydx[0] = state.Velocity(); dydx[2] = state.AngularVelocity(1); dydx[4] = state.AngularVelocity(2); diff --git a/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp b/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp index 06938a5425..f17e60b640 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp @@ -37,7 +37,7 @@ class ContinuousMountainCar /** * Construct a state instance. */ - State() : data(dimension, internal_compact::fill::zeros) + State() : data(dimension, arma::fill::zeros) { /* Nothing to do here. */ } /** diff --git a/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp b/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp index 5fbc6d000b..fc470a56e0 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp @@ -162,7 +162,7 @@ class DoublePoleCart // Update the number of steps performed. stepsPerformed++; - arma::vec dydx(6, internal_compact::fill::zeros); + arma::vec dydx(6, arma::fill::zeros); dydx[0] = state.Velocity(); dydx[2] = state.AngularVelocity(1); dydx[4] = state.AngularVelocity(2); diff --git a/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp b/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp index babbb67388..3698af6d81 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp @@ -36,7 +36,7 @@ class MountainCar /** * Construct a state instance. */ - State(): data(dimension, internal_compact::fill::zeros) + State(): data(dimension, arma::fill::zeros) { /* Nothing to do here. */ } /** diff --git a/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp b/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp index b4abb1c72c..bf44b6ac6d 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp @@ -39,7 +39,7 @@ class Pendulum /** * Construct a state instance. */ - State() : theta(0), data(dimension, internal_compact::fill::zeros) + State() : theta(0), data(dimension, arma::fill::zeros) { /* Nothing to do here. */ } /** diff --git a/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp b/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp index 94e36421b6..fa21e97d9e 100644 --- a/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp +++ b/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp @@ -282,7 +282,7 @@ class NStepQLearningWorker { // Initialize the gradient storage. arma::mat totalGradients(learningNetwork.Parameters().n_rows, - learningNetwork.Parameters().n_cols, internal_compact::fill::zeros); + learningNetwork.Parameters().n_cols, arma::fill::zeros); // Bootstrap from the value of next state. arma::colvec actionValue; diff --git a/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp b/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp index 2163404ea4..75e1bed513 100644 --- a/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp +++ b/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp @@ -282,7 +282,7 @@ class OneStepQLearningWorker { // Initialize the gradient storage. arma::mat totalGradients(learningNetwork.Parameters().n_rows, - learningNetwork.Parameters().n_cols, internal_compact::fill::zeros); + learningNetwork.Parameters().n_cols, arma::fill::zeros); for (size_t i = 0; i < pending.size(); ++i) { TransitionType &transition = pending[i]; diff --git a/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp b/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp index 6e115f820a..814b499898 100644 --- a/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp +++ b/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp @@ -295,7 +295,7 @@ class OneStepSarsaWorker { // Initialize the gradient storage. arma::mat totalGradients(learningNetwork.Parameters().n_rows, - learningNetwork.Parameters().n_cols, internal_compact::fill::zeros); + learningNetwork.Parameters().n_cols, arma::fill::zeros); for (size_t i = 0; i < pending.size(); ++i) { TransitionType &transition = pending[i]; diff --git a/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp b/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp index 697015d379..1029fa06af 100644 --- a/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp +++ b/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp @@ -87,7 +87,7 @@ class SSELoss { // Calculate gradients and hessians. gradients = (input.row(1) - input.row(0)).t(); - hessians = arma::vec(input.n_cols, internal_compact::fill::ones); + hessians = arma::vec(input.n_cols, arma::fill::ones); return std::pow(ApplyL1(accu(gradients)), 2) / (accu(hessians) + lambda); } From 04fcf6e07750b4a74f06ffcf51c8ad9a5f47a352 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 29 May 2024 18:42:33 +0200 Subject: [PATCH 009/212] Use GetFillType in the ann code base Signed-off-by: Omar Shrit --- src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp | 2 +- src/mlpack/methods/ann/ffn_impl.hpp | 2 +- src/mlpack/methods/ann/layer/convolution_impl.hpp | 2 +- src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp | 2 +- src/mlpack/methods/ann/layer/repeat_impl.hpp | 2 +- src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp index 21abe51a2a..47beaf9735 100644 --- a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp +++ b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp @@ -132,7 +132,7 @@ class NaiveConvolution // Pad filter and input to the working output shape. InMatType inputPadded(input.n_rows + 2 * paddingRows, - input.n_cols + 2 * paddingCols, arma::fill::zeros); + input.n_cols + 2 * paddingCols, GetFillType::zeros); inputPadded.submat(paddingRows, paddingCols, paddingRows + input.n_rows - 1, paddingCols + input.n_cols - 1) = input; diff --git a/src/mlpack/methods/ann/ffn_impl.hpp b/src/mlpack/methods/ann/ffn_impl.hpp index 63e468fa81..8e1bfdbb5f 100644 --- a/src/mlpack/methods/ann/ffn_impl.hpp +++ b/src/mlpack/methods/ann/ffn_impl.hpp @@ -471,7 +471,7 @@ typename MatType::elem_type FFN< typename MatType::elem_type res = 0; res += EvaluateWithGradient(parameters, 0, gradient, 1); MatType tmpGradient(gradient.n_rows, gradient.n_cols, - arma::fill::none); + GetFillType::none); for (size_t i = 1; i < predictors.n_cols; ++i) { res += EvaluateWithGradient(parameters, i, tmpGradient, 1); diff --git a/src/mlpack/methods/ann/layer/convolution_impl.hpp b/src/mlpack/methods/ann/layer/convolution_impl.hpp index c40a703f2c..b52ccf967f 100644 --- a/src/mlpack/methods/ann/layer/convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/convolution_impl.hpp @@ -422,7 +422,7 @@ void ConvolutionType< } MatType output(apparentWidth * apparentHeight * inMaps * higherInDimensions, - batchSize, arma::fill::zeros); + batchSize, GetFillType::zeros); CubeType outputCube; MakeAlias(outputCube, output, apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); diff --git a/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp b/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp index 6c60e9b128..aa6e9af354 100644 --- a/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp @@ -440,7 +440,7 @@ void GroupedConvolutionType< } MatType output(apparentWidth * apparentHeight * inMaps * higherInDimensions, - batchSize, arma::fill::zeros); + batchSize, GetFillType::zeros); CubeType outputCube; MakeAlias(outputCube, output, apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); diff --git a/src/mlpack/methods/ann/layer/repeat_impl.hpp b/src/mlpack/methods/ann/layer/repeat_impl.hpp index ea5a83f93c..e6a35ae303 100644 --- a/src/mlpack/methods/ann/layer/repeat_impl.hpp +++ b/src/mlpack/methods/ann/layer/repeat_impl.hpp @@ -156,7 +156,7 @@ void RepeatType::ComputeOutputDimensions() // element to the input elements. This will be used in the backward // pass with a simple matrix multiplication. backIdxs.set_size(inputSize, sizeMult); - UintCol counts(inputSize, arma::fill::zeros); + UintCol counts(inputSize, GetFillType::zeros); for (size_t i = 0; i < outIdxs.n_elem; i++) { arma::uword r = outIdxs.at(i); diff --git a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp index 5b24b371f2..fe79182a88 100644 --- a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp +++ b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp @@ -31,7 +31,7 @@ typename MatType::elem_type HingeLossType::Forward( const MatType& target) { MatType temp = target - (target == 0); - MatType tempZeros(size(target), arma::fill::zeros); + MatType tempZeros(size(target), GetFillType::zeros); MatType loss = max(tempZeros, 1 - prediction % temp); From 23b25b57af774741a1f7ef267d742926a389957c Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 29 May 2024 19:05:10 +0200 Subject: [PATCH 010/212] Fix missed type Signed-off-by: Omar Shrit --- src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp index 47beaf9735..20d776d426 100644 --- a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp +++ b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp @@ -132,7 +132,7 @@ class NaiveConvolution // Pad filter and input to the working output shape. InMatType inputPadded(input.n_rows + 2 * paddingRows, - input.n_cols + 2 * paddingCols, GetFillType::zeros); + input.n_cols + 2 * paddingCols, GetFillType::zeros); inputPadded.submat(paddingRows, paddingCols, paddingRows + input.n_rows - 1, paddingCols + input.n_cols - 1) = input; From 8022c0e13dd75ae6b5fa7e2cc320d908226c15ce Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Thu, 30 May 2024 17:38:36 +0200 Subject: [PATCH 011/212] Let see if this is going to make Mr CI happy Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 107 ++++++++++++++++----------------- 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 97e97d1fd2..416296b62e 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -76,63 +76,62 @@ namespace mlpack { using coot::zeros; #endif - -// By default, assume that we are using an Armadillo object. - template - struct GetFillType - { - static constexpr decltype(arma::fill::none) none = arma::fill::none; - static constexpr decltype(arma::fill::zeros) zeros = arma::fill::zeros; - static constexpr decltype(arma::fill::ones) ones = arma::fill::ones; - static constexpr decltype(arma::fill::randu) randu = arma::fill::randu; - static constexpr decltype(arma::fill::randn) randn = arma::fill::randn; - }; -#ifdef MLPACK_HAS_COOT - // If the matrix type is a Bandicoot type, use Bandicoot fill objects instead. - template::value>::type*> - struct GetFillType - { - static constexpr decltype(coot::fill::none) none = coot::fill::none; - static constexpr decltype(coot::fill::zeros) zeros = coot::fill::zeros; - static constexpr decltype(coot::fill::ones) ones = coot::fill::ones; - static constexpr decltype(coot::fill::randu) randu = coot::fill::randu; - static constexpr decltype(coot::fill::randn) randn = coot::fill::randn; - }; +#if (ARMA_VERSION_MAJOR >= 12) + // By default, assume that we are using an Armadillo object. + template + struct GetFillType + { + static constexpr decltype(arma::fill::none) none = arma::fill::none; + static constexpr decltype(arma::fill::zeros) zeros = arma::fill::zeros; + static constexpr decltype(arma::fill::ones) ones = arma::fill::ones; + static constexpr decltype(arma::fill::randu) randu = arma::fill::randu; + static constexpr decltype(arma::fill::randn) randn = arma::fill::randn; + }; + + #ifdef MLPACK_HAS_COOT + // If the matrix type is a Bandicoot type, use Bandicoot fill objects instead. + template::value>::type*> + struct GetFillType + { + static constexpr decltype(coot::fill::none) none = coot::fill::none; + static constexpr decltype(coot::fill::zeros) zeros = coot::fill::zeros; + static constexpr decltype(coot::fill::ones) ones = coot::fill::ones; + static constexpr decltype(coot::fill::randu) randu = coot::fill::randu; + static constexpr decltype(coot::fill::randn) randn = coot::fill::randn; + }; + #endif + +#else + + // By default, assume that we are using an Armadillo object. + template + struct GetFillType + { + static const decltype(arma::fill::none) none = arma::fill::none; + static const decltype(arma::fill::zeros) zeros = arma::fill::zeros; + static const decltype(arma::fill::ones) ones = arma::fill::ones; + static const decltype(arma::fill::randu) randu = arma::fill::randu; + static const decltype(arma::fill::randn) randn = arma::fill::randn; + }; + + #ifdef MLPACK_HAS_COOT + // If the matrix type is a Bandicoot type, use Bandicoot fill objects instead. + template::value>::type*> + struct GetFillType + { + static const decltype(coot::fill::none) none = coot::fill::none; + static const decltype(coot::fill::zeros) zeros = coot::fill::zeros; + static const decltype(coot::fill::ones) ones = coot::fill::ones; + static const decltype(coot::fill::randu) randu = coot::fill::randu; + static const decltype(coot::fill::randn) randn = coot::fill::randn; + }; + #endif + #endif - //namespace internal_compat { - - //namespace fill { - - //#ifdef MLPACK_HAS_COOT - //struct fill_none : public decltype(arma::fill::none), - //public decltype(coot::fill::none) { }; - - //struct fill_zeros : public decltype(arma::fill::zeros), - //public decltype(coot::fill::zeros) { }; - - //struct fill_ones : public decltype(arma::fill::ones), - //public decltype(coot::fill::ones) { }; - - //struct fill_randu : public decltype(arma::fill::randu), - //public decltype(coot::fill::randu) { }; - - //#else - //struct fill_none : public decltype(arma::fill::none) { }; - //struct fill_zeros : public decltype(arma::fill::zeros) { }; - //struct fill_ones : public decltype(arma::fill::ones) { }; - //struct fill_randu : public decltype(arma::fill::randu) { }; - //#endif - - //static constexpr fill_none none; - //static constexpr fill_zeros zeros; - //static constexpr fill_ones ones; - //static constexpr fill_randu randu; - - //} // namespace mlpack::GetFillType - //} // namespace mlpack::internal_compat } // namespace mlpack #endif From 877aff27cd8b18265e41f365605b2db1cdcacdc2 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Thu, 30 May 2024 17:46:15 +0200 Subject: [PATCH 012/212] g++ does not like static const, only static constexpr Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 416296b62e..8d66651a11 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -109,11 +109,11 @@ namespace mlpack { template struct GetFillType { - static const decltype(arma::fill::none) none = arma::fill::none; - static const decltype(arma::fill::zeros) zeros = arma::fill::zeros; - static const decltype(arma::fill::ones) ones = arma::fill::ones; - static const decltype(arma::fill::randu) randu = arma::fill::randu; - static const decltype(arma::fill::randn) randn = arma::fill::randn; + const decltype(arma::fill::none) none = arma::fill::none; + const decltype(arma::fill::zeros) zeros = arma::fill::zeros; + const decltype(arma::fill::ones) ones = arma::fill::ones; + const decltype(arma::fill::randu) randu = arma::fill::randu; + const decltype(arma::fill::randn) randn = arma::fill::randn; }; #ifdef MLPACK_HAS_COOT @@ -122,11 +122,11 @@ namespace mlpack { typename = typename std::enable_if::value>::type*> struct GetFillType { - static const decltype(coot::fill::none) none = coot::fill::none; - static const decltype(coot::fill::zeros) zeros = coot::fill::zeros; - static const decltype(coot::fill::ones) ones = coot::fill::ones; - static const decltype(coot::fill::randu) randu = coot::fill::randu; - static const decltype(coot::fill::randn) randn = coot::fill::randn; + const decltype(coot::fill::none) none = coot::fill::none; + const decltype(coot::fill::zeros) zeros = coot::fill::zeros; + const decltype(coot::fill::ones) ones = coot::fill::ones; + const decltype(coot::fill::randu) randu = coot::fill::randu; + const decltype(coot::fill::randn) randn = coot::fill::randn; }; #endif From 9f67e9929863bacbdb6c0b9dea33bf35852e8e79 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Thu, 30 May 2024 18:33:47 +0200 Subject: [PATCH 013/212] Let us see if this is passes Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 8d66651a11..05a53c20a2 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -109,11 +109,11 @@ namespace mlpack { template struct GetFillType { - const decltype(arma::fill::none) none = arma::fill::none; - const decltype(arma::fill::zeros) zeros = arma::fill::zeros; - const decltype(arma::fill::ones) ones = arma::fill::ones; - const decltype(arma::fill::randu) randu = arma::fill::randu; - const decltype(arma::fill::randn) randn = arma::fill::randn; + static decltype(arma::fill::none) none = arma::fill::none; + static decltype(arma::fill::zeros) zeros = arma::fill::zeros; + static decltype(arma::fill::ones) ones = arma::fill::ones; + static decltype(arma::fill::randu) randu = arma::fill::randu; + static decltype(arma::fill::randn) randn = arma::fill::randn; }; #ifdef MLPACK_HAS_COOT @@ -122,11 +122,11 @@ namespace mlpack { typename = typename std::enable_if::value>::type*> struct GetFillType { - const decltype(coot::fill::none) none = coot::fill::none; - const decltype(coot::fill::zeros) zeros = coot::fill::zeros; - const decltype(coot::fill::ones) ones = coot::fill::ones; - const decltype(coot::fill::randu) randu = coot::fill::randu; - const decltype(coot::fill::randn) randn = coot::fill::randn; + static decltype(coot::fill::none) none = coot::fill::none; + static decltype(coot::fill::zeros) zeros = coot::fill::zeros; + static decltype(coot::fill::ones) ones = coot::fill::ones; + static decltype(coot::fill::randu) randu = coot::fill::randu; + static decltype(coot::fill::randn) randn = coot::fill::randn; }; #endif From 5ba8e60ee29acc6fced692121a2fedb8b68e6e04 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 5 Jun 2024 13:49:47 +0200 Subject: [PATCH 014/212] Add more optimisation flags, fix a couple of bugs, add more platforms Signed-off-by: Omar Shrit --- board/flags-config.cmake | 48 ++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/board/flags-config.cmake b/board/flags-config.cmake index 94b2060b44..17d6f0ae3c 100644 --- a/board/flags-config.cmake +++ b/board/flags-config.cmake @@ -4,7 +4,8 @@ # footprints. # Set generic minimization flags for all platforms. -# These flags are the same for all cross-compilation cases. +# These flags are the same for all cross-compilation cases and they are +# mainly to reduce the binary footprint. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -fdata-sections -ffunction-sections") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer -fno-unwind-tables") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-asynchronous-unwind-tables -fvisibility=hidden") @@ -19,35 +20,58 @@ set(BOARD_NAME "" CACHE STRING "Specify Board name to optimize for.") string(TOUPPER ${BOARD_NAME} BOARD) # Set specific platforms CMAKE CXX flags. -if(BOARD MATCHES "RPI0" OR BOARD MATCHES "RPI1") +if(BOARD MATCHES "RPI0" OR BOARD MATCHES "RPI1" OR BOARD MATCHES "ARM11") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=arm1176jzf-s") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "ARMV6") set(OPENBLAS_BINARY "32") -elseif(BOARD MATCHES "RPI2") +elseif(BOARD MATCHES "RPI2" OR BOARD MATCHES "CORTEXA7") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a7") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon-vfpv4") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "ARMV7") set(OPENBLAS_BINARY "32") -elseif(BOARD MATCHES "RPI3") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a53") +elseif(BOARD MATCHES "CORTEXA8") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a8") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + set(OPENBLAS_TARGET "ARMV7") + set(OPENBLAS_BINARY "32") +elseif(BOARD MATCHES "CORTEXA9") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a9") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + set(OPENBLAS_TARGET "CORTEXA9") + set(OPENBLAS_BINARY "32") +elseif(BOARD MATCHES "CORTEXA15") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a15") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + set(OPENBLAS_TARGET "CORTEXA15") + set(OPENBLAS_BINARY "32") +elseif(BOARD MATCHES "RPI3" OR BOARD MATCHES "CORTEXA53") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a53 -mfloat-abi=hard") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon-fp-armv8 -mneon-for-64bit") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "CORTEXA53") set(OPENBLAS_BINARY "64") -elseif(BOARD MATCHES "RPI4") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a72") +elseif(BOARD MATCHES "RPI4" OR BOARD MATCHES "CORTEXA72") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a72 -mfloat-abi=hard") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon-fp-armv8 -mneon-for-64bit") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "CORTEXA72") set(OPENBLAS_BINARY "64") +elseif(BOARD MATCHES "JETSONAGX" OR BOARD MATCHES "CORTEXA76") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a76 -mfloat-abi=hard") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon-fp-armv8 -mneon-for-64bit") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + set(OPENBLAS_TARGET "CORTEXA76") + set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "BV") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "RISCV64_GENERIC") set(OPENBLAS_BINARY "64") -elseif(BOARD MATCHES "JETSONAGX") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -matune=cortex-a76") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") - set(OPENBLAS_TARGET "ARM8") - set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "KATAMI") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium3") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") From b0ef01a11daaad4ac58f6ed51fb0234893794517 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 5 Jun 2024 17:42:27 +0200 Subject: [PATCH 015/212] Add two risc-v options Signed-off-by: Omar Shrit --- board/flags-config.cmake | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/board/flags-config.cmake b/board/flags-config.cmake index 17d6f0ae3c..9789bd7aa7 100644 --- a/board/flags-config.cmake +++ b/board/flags-config.cmake @@ -72,6 +72,16 @@ elseif(BOARD MATCHES "BV") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "RISCV64_GENERIC") set(OPENBLAS_BINARY "64") +elseif(BOARD MATCHES "C906") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=thead-c906") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + set(OPENBLAS_TARGET "RISCV64_GENERIC") + set(OPENBLAS_BINARY "64") +elseif(BOARD MATCHES "x280") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=sifive-x280") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + set(OPENBLAS_TARGET "RISCV64_GENERIC") + set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "KATAMI") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium3") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") From b4a878b4b633246fa12d90ee51a6b37e7c9a66bf Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 5 Jun 2024 18:28:37 +0200 Subject: [PATCH 016/212] OpenBLAS does support x280 Signed-off-by: Omar Shrit --- board/flags-config.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/board/flags-config.cmake b/board/flags-config.cmake index 9789bd7aa7..c8c402dbed 100644 --- a/board/flags-config.cmake +++ b/board/flags-config.cmake @@ -80,7 +80,7 @@ elseif(BOARD MATCHES "C906") elseif(BOARD MATCHES "x280") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=sifive-x280") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") - set(OPENBLAS_TARGET "RISCV64_GENERIC") + set(OPENBLAS_TARGET "x280") set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "KATAMI") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium3") From 3d973c163718eb0dab86300368e72ded02d6e726 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 7 Jun 2024 08:02:04 +0200 Subject: [PATCH 017/212] Disable -O3 since we need to use -Os, also increase openblas min version Signed-off-by: Omar Shrit --- CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 75f6fe1b9d..69e7344903 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,7 +249,9 @@ if (DEBUG) else() add_definitions(-DNDEBUG) if (NOT MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") + if (NOT CMAKE_CROSSCOMPILING) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") + endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O3") else () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O3") @@ -283,7 +285,7 @@ endif() # Download and compile OpenBLAS if we are cross compiling mlpack for a specific # architecture. The function takes the version of OpenBLAS as variable. if (CMAKE_CROSSCOMPILING) - search_openblas(0.3.13) + search_openblas(0.3.26) endif() if (NOT DOWNLOAD_DEPENDENCIES) From 1554508060df63817ab4a659805baf77b9bf215e Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 7 Jun 2024 09:34:42 +0200 Subject: [PATCH 018/212] Add various flags to reduce the bianry size Signed-off-by: Omar Shrit --- board/flags-config.cmake | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/board/flags-config.cmake b/board/flags-config.cmake index c8c402dbed..ec38e994f6 100644 --- a/board/flags-config.cmake +++ b/board/flags-config.cmake @@ -6,15 +6,18 @@ # Set generic minimization flags for all platforms. # These flags are the same for all cross-compilation cases and they are # mainly to reduce the binary footprint. -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -fdata-sections -ffunction-sections") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s -fdata-sections -ffunction-sections") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer -fno-unwind-tables") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-asynchronous-unwind-tables -fvisibility=hidden") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fshort-enums -finline-small-functions") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -findirect-inlining -fno-common") -#-flto -fuse-ld=gold # There is an issue with gold link when compiling on -# Ubuntu 16. At that point gcc linker did not integrate the flto support -# inside and it was a separate plugin that need to be added. Therefore, -# this can be added when mlpack Azure CI moves toward Ubuntu 20. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmerge-all-constants -fno-ident") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-unroll-loops -fno-math-errno") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-stack-protector -Wl,-z,norelro") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -Wl,--hash-style=gnu -Wl,--build-id=none") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostartfiles") ## this get us 400KB alone +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-nmagic,-Bsymbolic") + set(BOARD_NAME "" CACHE STRING "Specify Board name to optimize for.") string(TOUPPER ${BOARD_NAME} BOARD) From 3c415cf1c9a4a022f78be2330e70177503762080 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Sat, 8 Jun 2024 20:14:24 +0200 Subject: [PATCH 019/212] Optimize better for binary footprint Signed-off-by: Omar Shrit --- CMake/ConfigureCrossCompile.cmake | 1 + CMakeLists.txt | 2 +- board/flags-config.cmake | 27 ++++++++------------------- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/CMake/ConfigureCrossCompile.cmake b/CMake/ConfigureCrossCompile.cmake index a320d32932..0a39b829ed 100644 --- a/CMake/ConfigureCrossCompile.cmake +++ b/CMake/ConfigureCrossCompile.cmake @@ -28,6 +28,7 @@ macro(search_openblas version) get_deps(https://github.com/xianyi/OpenBLAS/releases/download/v${version}/OpenBLAS-${version}.tar.gz OpenBLAS OpenBLAS-${version}.tar.gz) if (NOT MSVC) if (NOT EXISTS "${CMAKE_BINARY_DIR}/deps/OpenBLAS-${version}/libopenblas.a") + set(ENV{COMMON_OPT} "${CMAKE_OPENBLAS_FLAGS}") # Pass our flags to OpenBLAS execute_process(COMMAND make TARGET=${OPENBLAS_TARGET} BINARY=${OPENBLAS_BINARY} HOSTCC=gcc CC=${CMAKE_C_COMPILER} FC=${CMAKE_FORTRAN_COMPILER} NO_SHARED=1 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/deps/OpenBLAS-${version}) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 69e7344903..7ff506f01f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,7 +327,7 @@ if (StbImage_FOUND) # Make sure that we can link STB in multiple translation units. include(CMake/TestStaticSTB.cmake) - if (NOT CMAKE_HAS_WORKING_STATIC_STB) + if (NOT CMAKE_HAS_WORKING_STATIC_STB AND NOT CMAKE_CROSSCOMPILING) message(FATAL_ERROR "STB implementations's static mode cannot link across " "multiple translation units! Try upgrading your STB implementation, " "or using the auto-downloader (set DOWNLOAD_DEPENDENCIES=ON in the " diff --git a/board/flags-config.cmake b/board/flags-config.cmake index ec38e994f6..0f661805d2 100644 --- a/board/flags-config.cmake +++ b/board/flags-config.cmake @@ -13,11 +13,14 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fshort-enums -finline-small-functions") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -findirect-inlining -fno-common") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmerge-all-constants -fno-ident") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-unroll-loops -fno-math-errno") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-stack-protector -Wl,-z,norelro") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -Wl,--hash-style=gnu -Wl,--build-id=none") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostartfiles") ## this get us 400KB alone -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-nmagic,-Bsymbolic") - +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-stack-protector") +set(CMAKE_OPENBLAS_FLAGS "${CMAKE_CXX_FLAGS}") # OpenBLAS does not supoport flto +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--hash-style=gnu -Wl,--build-id=none") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,norelro") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +## Keep the following flag in comment, it will be relevant in the case of MCU's +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-nmagic,-Bsymbolic -nostartfiles") set(BOARD_NAME "" CACHE STRING "Specify Board name to optimize for.") string(TOUPPER ${BOARD_NAME} BOARD) @@ -26,78 +29,64 @@ string(TOUPPER ${BOARD_NAME} BOARD) if(BOARD MATCHES "RPI0" OR BOARD MATCHES "RPI1" OR BOARD MATCHES "ARM11") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=arm1176jzf-s") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "ARMV6") set(OPENBLAS_BINARY "32") elseif(BOARD MATCHES "RPI2" OR BOARD MATCHES "CORTEXA7") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a7") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon-vfpv4") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "ARMV7") set(OPENBLAS_BINARY "32") elseif(BOARD MATCHES "CORTEXA8") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a8") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "ARMV7") set(OPENBLAS_BINARY "32") elseif(BOARD MATCHES "CORTEXA9") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a9") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "CORTEXA9") set(OPENBLAS_BINARY "32") elseif(BOARD MATCHES "CORTEXA15") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a15") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "CORTEXA15") set(OPENBLAS_BINARY "32") elseif(BOARD MATCHES "RPI3" OR BOARD MATCHES "CORTEXA53") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a53 -mfloat-abi=hard") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon-fp-armv8 -mneon-for-64bit") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "CORTEXA53") set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "RPI4" OR BOARD MATCHES "CORTEXA72") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a72 -mfloat-abi=hard") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon-fp-armv8 -mneon-for-64bit") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "CORTEXA72") set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "JETSONAGX" OR BOARD MATCHES "CORTEXA76") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a76 -mfloat-abi=hard") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon-fp-armv8 -mneon-for-64bit") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "CORTEXA76") set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "BV") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "RISCV64_GENERIC") set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "C906") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=thead-c906") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "RISCV64_GENERIC") set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "x280") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=sifive-x280") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "x280") set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "KATAMI") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium3") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "KATAMI") set(OPENBLAS_BINARY "32") elseif(BOARD MATCHES "COPPERMINE") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium3") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "COPPERMINE") set(OPENBLAS_BINARY "32") elseif(BOARD MATCHES "NORTHWOOD") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium4") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set(OPENBLAS_TARGET "NORTHWOOD") set(OPENBLAS_BINARY "32") elseif(BOARD) From 8e3360dba78393cbac0b647573306de828b9e0b3 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Mon, 10 Jun 2024 12:39:13 +0200 Subject: [PATCH 020/212] Apply the same flags to C as well Signed-off-by: Omar Shrit --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ff506f01f..d5cc05c8ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -251,8 +251,9 @@ else() if (NOT MSVC) if (NOT CMAKE_CROSSCOMPILING) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O3") endif() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") else () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O3") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /O3") From 60376de1ba8053eb25ae4f5600279148aa6129b0 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Mon, 10 Jun 2024 13:02:24 +0200 Subject: [PATCH 021/212] Add const to static Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 05a53c20a2..416296b62e 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -109,11 +109,11 @@ namespace mlpack { template struct GetFillType { - static decltype(arma::fill::none) none = arma::fill::none; - static decltype(arma::fill::zeros) zeros = arma::fill::zeros; - static decltype(arma::fill::ones) ones = arma::fill::ones; - static decltype(arma::fill::randu) randu = arma::fill::randu; - static decltype(arma::fill::randn) randn = arma::fill::randn; + static const decltype(arma::fill::none) none = arma::fill::none; + static const decltype(arma::fill::zeros) zeros = arma::fill::zeros; + static const decltype(arma::fill::ones) ones = arma::fill::ones; + static const decltype(arma::fill::randu) randu = arma::fill::randu; + static const decltype(arma::fill::randn) randn = arma::fill::randn; }; #ifdef MLPACK_HAS_COOT @@ -122,11 +122,11 @@ namespace mlpack { typename = typename std::enable_if::value>::type*> struct GetFillType { - static decltype(coot::fill::none) none = coot::fill::none; - static decltype(coot::fill::zeros) zeros = coot::fill::zeros; - static decltype(coot::fill::ones) ones = coot::fill::ones; - static decltype(coot::fill::randu) randu = coot::fill::randu; - static decltype(coot::fill::randn) randn = coot::fill::randn; + static const decltype(coot::fill::none) none = coot::fill::none; + static const decltype(coot::fill::zeros) zeros = coot::fill::zeros; + static const decltype(coot::fill::ones) ones = coot::fill::ones; + static const decltype(coot::fill::randu) randu = coot::fill::randu; + static const decltype(coot::fill::randn) randn = coot::fill::randn; }; #endif From 46057e7e14eaaa6ed8d132c72a5dc10c0cff8e8c Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 12 Jun 2024 21:55:43 +0200 Subject: [PATCH 022/212] feat: optimize the backward convolution --- .../methods/ann/layer/convolution_impl.hpp | 74 ++++++++----------- 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/src/mlpack/methods/ann/layer/convolution_impl.hpp b/src/mlpack/methods/ann/layer/convolution_impl.hpp index c40a703f2c..6f5ac8a1b5 100644 --- a/src/mlpack/methods/ann/layer/convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/convolution_impl.hpp @@ -382,41 +382,34 @@ void ConvolutionType< inMaps * higherInDimensions * batchSize); gTemp.zeros(); - const bool usingPadding = - (padWLeft != 0 || padWRight != 0 || padHTop != 0 || padHBottom != 0); + const bool usingPadding = (padWLeft != 0 || padWRight != 0 || padHTop != 0 || padHBottom != 0); - // To perform the backward pass, we need to rotate all the filters. - CubeType rotatedFilters(weight.n_rows, - weight.n_cols, weight.n_slices); - - // To perform the backward pass, we need to dilate all the mappedError. - CubeType dilatedMappedError; - if (strideHeight == 1 && strideWidth == 1) + CubeType dilatedMappedError(mappedError.n_rows * (strideWidth == 1 ? 1 : strideWidth - 1), + mappedError.n_cols * (strideHeight == 1 ? 1 : strideHeight - 1), + mappedError.n_slices); + #pragma omp parallel for collapse(3) + for (size_t i = 0; i < mappedError.n_slices; ++i) { - MakeAlias(dilatedMappedError, mappedError, mappedError.n_rows, - mappedError.n_cols, mappedError.n_slices); - } - else - { - dilatedMappedError.zeros(mappedError.n_rows * strideWidth - - (strideWidth - 1), mappedError.n_cols * strideHeight - - (strideHeight - 1), mappedError.n_slices); - #pragma omp parallel for collapse(3) - for (size_t i = 0; i < mappedError.n_slices; ++i) + for (size_t j = 0; j < mappedError.n_cols; ++j) { - for (size_t j = 0; j < mappedError.n_cols; ++j) + for (size_t k = 0; k < mappedError.n_rows; ++k) { - for (size_t k = 0; k < mappedError.n_rows; ++k) + if (strideHeight > 1 || strideWidth > 1) { dilatedMappedError(k * strideWidth, j * strideHeight, i) = mappedError(k, j, i); } + else + { + dilatedMappedError(k, j, i) = mappedError(k, j, i); + } } } } + CubeType rotatedFilters(weight.n_rows, weight.n_cols, weight.n_slices); #pragma omp parallel for - for (size_t map = 0; map < (size_t) (maps * inMaps); ++map) + for (size_t map = 0; map < weight.n_slices; ++map) { Rotate180(weight.slice(map), rotatedFilters.slice(map)); } @@ -427,31 +420,25 @@ void ConvolutionType< MakeAlias(outputCube, output, apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); - // See Forward() for the overall iteration strategy. + #pragma omp parallel for collapse(2) for (size_t offset = 0; offset < (higherInDimensions * batchSize); ++offset) { - const size_t fullInputOffset = offset * inMaps; - const size_t fullOutputOffset = offset * maps; - - // Iterate over input maps. - #pragma omp parallel for - for (size_t inMap = 0; inMap < (size_t) inMaps; ++inMap) - { - // Iterate over output maps. - MatType& curG = outputCube.slice(inMap + fullInputOffset); - for (size_t outMap = 0; outMap < maps; ++outMap) + for (size_t inMap = 0; inMap < (size_t) inMaps; ++inMap) { - BackwardConvolutionRule::Convolution( - dilatedMappedError.slice(outMap + fullOutputOffset), - rotatedFilters.slice((outMap * inMaps) + inMap), - curG, - 1, - 1, - 1, - 1, - true); + MatType& curG = outputCube.slice(inMap + offset * inMaps); + for (size_t outMap = 0; outMap < maps; ++outMap) + { + BackwardConvolutionRule::Convolution( + dilatedMappedError.slice(outMap + offset * maps), + rotatedFilters.slice((outMap * inMaps) + inMap), + curG, + 1, + 1, + 1, + 1, + true); + } } - } } MatType temp(padding.OutputDimensions()[0] * padding.OutputDimensions()[1] * inMaps * higherInDimensions, batchSize); @@ -472,7 +459,6 @@ void ConvolutionType< gTemp = tempCube; } } - template< typename ForwardConvolutionRule, typename BackwardConvolutionRule, From 7111467cf5b7a0f1c9ae2fc5aadfd5075cea528f Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Sun, 16 Jun 2024 20:35:41 +0200 Subject: [PATCH 023/212] fix: conv impl gradient test --- src/mlpack/methods/ann/layer/convolution_impl.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/ann/layer/convolution_impl.hpp b/src/mlpack/methods/ann/layer/convolution_impl.hpp index 6f5ac8a1b5..607d393531 100644 --- a/src/mlpack/methods/ann/layer/convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/convolution_impl.hpp @@ -432,8 +432,8 @@ void ConvolutionType< dilatedMappedError.slice(outMap + offset * maps), rotatedFilters.slice((outMap * inMaps) + inMap), curG, - 1, - 1, + strideWidth, + strideHeight, 1, 1, true); @@ -519,8 +519,8 @@ void ConvolutionType< tempCube.slice(inMap + fullInputOffset), curError, gradientTemp.slice((outMap * inMaps) + inMap), - 1, - 1, + strideWidth, + strideHeight, strideWidth, strideHeight, true); From c1505f214c62959f436b93f625469f092f855b35 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Sun, 16 Jun 2024 20:37:19 +0200 Subject: [PATCH 024/212] fix: line length --- src/mlpack/methods/ann/layer/convolution_impl.hpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/ann/layer/convolution_impl.hpp b/src/mlpack/methods/ann/layer/convolution_impl.hpp index 607d393531..209466701b 100644 --- a/src/mlpack/methods/ann/layer/convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/convolution_impl.hpp @@ -382,11 +382,15 @@ void ConvolutionType< inMaps * higherInDimensions * batchSize); gTemp.zeros(); - const bool usingPadding = (padWLeft != 0 || padWRight != 0 || padHTop != 0 || padHBottom != 0); + const bool usingPadding = + (padWLeft != 0 || padWRight != 0 || + padHTop != 0 || padHBottom != 0); - CubeType dilatedMappedError(mappedError.n_rows * (strideWidth == 1 ? 1 : strideWidth - 1), - mappedError.n_cols * (strideHeight == 1 ? 1 : strideHeight - 1), - mappedError.n_slices); + CubeType dilatedMappedError( + mappedError.n_rows * (strideWidth == 1 ? 1 : strideWidth - 1), + mappedError.n_cols * (strideHeight == 1 ? 1 : strideHeight - 1), + mappedError.n_slices + ); #pragma omp parallel for collapse(3) for (size_t i = 0; i < mappedError.n_slices; ++i) { From ad84e0d2fdee0c8a84987a4fd9a9cba39958c899 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Mon, 17 Jun 2024 11:59:19 +0200 Subject: [PATCH 025/212] Remove the STB unecessary flag Signed-off-by: Omar Shrit --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d5cc05c8ea..43bf312d01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -328,7 +328,7 @@ if (StbImage_FOUND) # Make sure that we can link STB in multiple translation units. include(CMake/TestStaticSTB.cmake) - if (NOT CMAKE_HAS_WORKING_STATIC_STB AND NOT CMAKE_CROSSCOMPILING) + if (NOT CMAKE_HAS_WORKING_STATIC_STB) message(FATAL_ERROR "STB implementations's static mode cannot link across " "multiple translation units! Try upgrading your STB implementation, " "or using the auto-downloader (set DOWNLOAD_DEPENDENCIES=ON in the " From 54645e454ae243acd4d04357ca1a2c97a6507f33 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Mon, 17 Jun 2024 12:23:45 +0200 Subject: [PATCH 026/212] We can not check working atomic for crosscompiled version Signed-off-by: Omar Shrit --- CMake/CheckAtomic.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMake/CheckAtomic.cmake b/CMake/CheckAtomic.cmake index 3985c9aa0a..2e62c5bdf0 100644 --- a/CMake/CheckAtomic.cmake +++ b/CMake/CheckAtomic.cmake @@ -70,7 +70,9 @@ if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64) if(HAVE_CXX_LIBATOMICS64) list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") - check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) + if (NOT CMAKE_CROSSCOMPILING) + check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) + endif() if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) message(FATAL_ERROR "Host compiler must support std::atomic!") endif() From a7e90eb3e5b793ccf0d58d38bce3baeca91e8229 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Mon, 17 Jun 2024 12:30:40 +0200 Subject: [PATCH 027/212] Ractify the condition to include the results Signed-off-by: Omar Shrit --- CMake/CheckAtomic.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMake/CheckAtomic.cmake b/CMake/CheckAtomic.cmake index 2e62c5bdf0..145518d4a3 100644 --- a/CMake/CheckAtomic.cmake +++ b/CMake/CheckAtomic.cmake @@ -72,9 +72,9 @@ if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") if (NOT CMAKE_CROSSCOMPILING) check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) - endif() - if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) - message(FATAL_ERROR "Host compiler must support std::atomic!") + if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) + message(FATAL_ERROR "Host compiler must support std::atomic!") + endif() endif() else() message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.") From dd0f3d4240ba3460383130c7f8b2f8ce865fd20a Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Tue, 18 Jun 2024 18:26:42 +0200 Subject: [PATCH 028/212] Use constexp Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 416296b62e..6627fb0e2a 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -109,11 +109,11 @@ namespace mlpack { template struct GetFillType { - static const decltype(arma::fill::none) none = arma::fill::none; - static const decltype(arma::fill::zeros) zeros = arma::fill::zeros; - static const decltype(arma::fill::ones) ones = arma::fill::ones; - static const decltype(arma::fill::randu) randu = arma::fill::randu; - static const decltype(arma::fill::randn) randn = arma::fill::randn; + static constexpr decltype(arma::fill::none) none = arma::fill::none; + static constexpr decltype(arma::fill::zeros) zeros = arma::fill::zeros; + static constexpr decltype(arma::fill::ones) ones = arma::fill::ones; + static constexpr decltype(arma::fill::randu) randu = arma::fill::randu; + static constexpr decltype(arma::fill::randn) randn = arma::fill::randn; }; #ifdef MLPACK_HAS_COOT @@ -122,11 +122,11 @@ namespace mlpack { typename = typename std::enable_if::value>::type*> struct GetFillType { - static const decltype(coot::fill::none) none = coot::fill::none; - static const decltype(coot::fill::zeros) zeros = coot::fill::zeros; - static const decltype(coot::fill::ones) ones = coot::fill::ones; - static const decltype(coot::fill::randu) randu = coot::fill::randu; - static const decltype(coot::fill::randn) randn = coot::fill::randn; + static constexpr decltype(coot::fill::none) none = coot::fill::none; + static constexpr decltype(coot::fill::zeros) zeros = coot::fill::zeros; + static constexpr decltype(coot::fill::ones) ones = coot::fill::ones; + static constexpr decltype(coot::fill::randu) randu = coot::fill::randu; + static constexpr decltype(coot::fill::randn) randn = coot::fill::randn; }; #endif From 19ccbad73500070a3f02735797e5842114564826 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Thu, 20 Jun 2024 19:18:32 +0100 Subject: [PATCH 029/212] adapting nearest interpolation layer --- src/mlpack/methods/ann/layer/layer_types.hpp | 1 + .../nearest_interpolation.hpp | 94 ++++++-------- .../nearest_interpolation_impl.hpp | 121 ++++++++++++++---- 3 files changed, 135 insertions(+), 81 deletions(-) rename src/mlpack/methods/ann/layer/{not_adapted => }/nearest_interpolation.hpp (66%) rename src/mlpack/methods/ann/layer/{not_adapted => }/nearest_interpolation_impl.hpp (53%) diff --git a/src/mlpack/methods/ann/layer/layer_types.hpp b/src/mlpack/methods/ann/layer/layer_types.hpp index 87a9141209..305f3cf0dd 100644 --- a/src/mlpack/methods/ann/layer/layer_types.hpp +++ b/src/mlpack/methods/ann/layer/layer_types.hpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include diff --git a/src/mlpack/methods/ann/layer/not_adapted/nearest_interpolation.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp similarity index 66% rename from src/mlpack/methods/ann/layer/not_adapted/nearest_interpolation.hpp rename to src/mlpack/methods/ann/layer/nearest_interpolation.hpp index 93fc6a09f0..278d5b8945 100644 --- a/src/mlpack/methods/ann/layer/not_adapted/nearest_interpolation.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp @@ -11,6 +11,7 @@ #define MLPACK_METHODS_ANN_LAYER_NEAREST_INTERPOLATION_HPP #include +#include "layer.hpp" namespace mlpack { @@ -21,35 +22,39 @@ namespace mlpack { * scaling purposes. The input should be a 2D matrix and it can have * a number of channels/units. * - * @tparam InputDataType Type of the input data (arma::colvec, arma::mat, + * @tparam InputType Type of the input data (arma::colvec, arma::mat, * arma::sp_mat or arma::cube). - * @tparam OutputDataType Type of the output data (arma::colvec, arma::mat, + * @tparam MatType Type of the output data (arma::colvec, arma::mat, * arma::sp_mat or arma::cube). */ -template < - typename InputDataType = arma::mat, - typename OutputDataType = arma::mat -> -class NearestInterpolation +template +class NearestInterpolationType : public Layer { public: - //! Create the NearestInterpolation object. - NearestInterpolation(); - /** - * The constructor for the NearestInterpolation. - * - * @param inRowSize Number of input rows. - * @param inColSize Number of input columns. - * @param outRowSize Number of output rows. - * @param outColSize Number of output columns. - * @param depth Number of input slices. - */ - NearestInterpolation(const size_t inRowSize, - const size_t inColSize, - const size_t outRowSize, - const size_t outColSize, - const size_t depth); + //! Create the NearestInterpolation object. + NearestInterpolationType(); + + NearestInterpolationType(const size_t inRowSize, + const size_t inColSize, + const size_t outRowSize, + const size_t outColSize, + const size_t depth); + + NearestInterpolationType* Clone() const { + return new NearestInterpolationType(*this); + } + + virtual ~NearestInterpolationType() { } + + //! Copy the given ConcatenateType layer. + NearestInterpolationType(const NearestInterpolationType& other); + //! Take ownership of the given ConcatenateType layer. + NearestInterpolationType(NearestInterpolationType&& other); + //! Copy the given ConcatenateType layer. + NearestInterpolationType& operator=(const NearestInterpolationType& other); + //! Take ownership of the given ConcatenateType layer. + NearestInterpolationType& operator=(NearestInterpolationType&& other); /** * Forward pass through the layer. The layer interpolates @@ -58,8 +63,7 @@ class NearestInterpolation * @param input The input matrix. * @param output The resulting interpolated output matrix. */ - template - void Forward(const arma::Mat& input, arma::Mat& output); + void Forward(const MatType& input, MatType& output); /** * Ordinary feed backward pass of a neural network, calculating the function @@ -72,52 +76,36 @@ class NearestInterpolation * @param gradient The computed backward gradient. * @param output The resulting down-sampled output. */ - template - void Backward(const arma::Mat& /*input*/, - const arma::Mat& gradient, - arma::Mat& output); - - //! Get the output parameter. - OutputDataType const& OutputParameter() const { return outputParameter; } - //! Modify the output parameter. - OutputDataType& OutputParameter() { return outputParameter; } - - //! Get the delta. - OutputDataType const& Delta() const { return delta; } - //! Modify the delta. - OutputDataType& Delta() { return delta; } + void Backward(const MatType& /*input*/, + const MatType& gradient, + MatType& output); + void ComputeOutputDimensions(); //! Get the row size of the input. size_t const& InRowSize() const { return inRowSize; } //! Modify the row size of the input. size_t& InRowSize() { return inRowSize; } - + //! Get the column size of the input. size_t const& InColSize() const { return inColSize; } //! Modify the column size of the input. size_t& InColSize() { return inColSize; } - + //! Get the row size of the output. size_t const& OutRowSize() const { return outRowSize; } //! Modify the row size of the output. size_t& OutRowSize() { return outRowSize; } - + //! Get the column size of the output. size_t const& OutColSize() const { return outColSize; } //! Modify the column size of the output. size_t& OutColSize() { return outColSize; } - + //! Get the depth of the input. size_t const& InDepth() const { return depth; } //! Modify the depth of the input. size_t& InDepth() { return depth; } - - //! Get the shape of the input. - size_t InputShape() const - { - return inRowSize; - } - + /** * Serialize the layer. */ @@ -137,12 +125,10 @@ class NearestInterpolation size_t depth; //! Locally stored number of input points. size_t batchSize; - //! Locally-stored delta object. - OutputDataType delta; - //! Locally-stored output parameter object. - OutputDataType outputParameter; }; // class NearestInterpolation +typedef NearestInterpolationType NearestInterpolation; + } // namespace mlpack // Include implementation. diff --git a/src/mlpack/methods/ann/layer/not_adapted/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp similarity index 53% rename from src/mlpack/methods/ann/layer/not_adapted/nearest_interpolation_impl.hpp rename to src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index 52a094b7fd..d90fdc2c5e 100644 --- a/src/mlpack/methods/ann/layer/not_adapted/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -17,10 +17,9 @@ namespace mlpack { - -template -NearestInterpolation:: - NearestInterpolation(): +template +NearestInterpolationType::NearestInterpolationType(): + Layer(), inRowSize(0), inColSize(0), outRowSize(0), @@ -31,27 +30,89 @@ NearestInterpolation:: // Nothing to do here. } -template -NearestInterpolation:: -NearestInterpolation(const size_t inRowSize, +template +NearestInterpolationType:: +NearestInterpolationType(const size_t inRowSize, const size_t inColSize, const size_t outRowSize, const size_t outColSize, const size_t depth) : - inRowSize(inRowSize), - inColSize(inColSize), - outRowSize(outRowSize), - outColSize(outColSize), - depth(depth), - batchSize(0) + Layer(), + inRowSize(inRowSize), + inColSize(inColSize), + outRowSize(outRowSize), + outColSize(outColSize), + depth(depth), + batchSize(0) { // Nothing to do here. } -template -template -void NearestInterpolation::Forward( - const arma::Mat& input, arma::Mat& output) +template +NearestInterpolationType:: +NearestInterpolationType(const NearestInterpolationType& other) : + Layer(), + inRowSize(other.inRowSize), + inColSize(other.inColSize), + outRowSize(other.outRowSize), + outColSize(other.outColSize), + depth(other.depth), + batchSize(other.batchSize) +{ + // Nothing to do here. +} + +template +NearestInterpolationType:: +NearestInterpolationType(NearestInterpolationType&& other) : + Layer(std::move(other)), + inRowSize(std::move(other.inRowSize)), + inColSize(std::move(other.inColSize)), + outRowSize(std::move(other.outRowSize)), + outColSize(std::move(other.outColSize)), + depth(std::move(other.depth)), + batchSize(std::move(other.batchSize)) +{ + // Nothing to do here. +} + +template +NearestInterpolationType& +NearestInterpolationType:: +operator=(const NearestInterpolationType& other) +{ + if (&other != this) { + Layer::operator=(other); + inRowSize = other.inRowSize; + inColSize = other.inColSize; + outRowSize = other.outRowSize; + outColSize = other.outColSize; + depth = other.depth; + batchSize = other.batchSize; + } + return *this; +} + +template +NearestInterpolationType& +NearestInterpolationType:: +operator=(NearestInterpolationType&& other) +{ + if (&other != this) { + Layer::operator=(std::move(other)); + inRowSize = std::move(other.inRowSize); + inColSize = std::move(other.inColSize); + outRowSize = std::move(other.outRowSize); + outColSize = std::move(other.outColSize); + depth = std::move(other.depth); + batchSize = std::move(other.batchSize); + } + return *this; +} + +template +void NearestInterpolationType::Forward( + const MatType& input, MatType& output) { batchSize = input.n_cols; if (output.is_empty()) @@ -65,7 +126,7 @@ void NearestInterpolation::Forward( assert(inRowSize >= 2); assert(inColSize >= 2); - arma::cube inputAsCube(const_cast&>(input).memptr(), + arma::cube inputAsCube(const_cast(input).memptr(), inRowSize, inColSize, depth * batchSize, false, false); arma::cube outputAsCube(output.memptr(), outRowSize, outColSize, depth * batchSize, false, true); @@ -90,12 +151,11 @@ void NearestInterpolation::Forward( } } -template -template -void NearestInterpolation::Backward( - const arma::Mat& /*input*/, - const arma::Mat& gradient, - arma::Mat& output) +template +void NearestInterpolationType::Backward( + const MatType& /*input*/, + const MatType& gradient, + MatType& output) { if (output.is_empty()) { @@ -112,7 +172,7 @@ void NearestInterpolation::Backward( arma::cube outputAsCube(output.memptr(), inRowSize, inColSize, depth * batchSize, false, true); - arma::cube gradientAsCube(((arma::Mat&) gradient).memptr(), outRowSize, + arma::cube gradientAsCube(((MatType&) gradient).memptr(), outRowSize, outColSize, depth * batchSize, false, false); double scaleRow = (double)(inRowSize) / outRowSize; @@ -142,9 +202,16 @@ void NearestInterpolation::Backward( } } -template +template +void NearestInterpolationType::ComputeOutputDimensions() +{ + this->outputDimensions[0] = outRowSize * outColSize * depth; + this->outputDimensions[1] = batchSize; +} + +template template -void NearestInterpolation::serialize( +void NearestInterpolationType::serialize( Archive& ar, const uint32_t /* version */) { ar(CEREAL_NVP(inRowSize)); From 4fc2b1dd08b193cb8e635efa030587f02134721e Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Fri, 21 Jun 2024 14:23:57 +0100 Subject: [PATCH 030/212] fix style and remove extra whitespace --- src/mlpack/methods/ann/layer/nearest_interpolation.hpp | 6 ------ .../methods/ann/layer/nearest_interpolation_impl.hpp | 8 ++++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp index 278d5b8945..8a4af66937 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp @@ -31,7 +31,6 @@ template class NearestInterpolationType : public Layer { public: - //! Create the NearestInterpolation object. NearestInterpolationType(); @@ -85,27 +84,22 @@ class NearestInterpolationType : public Layer size_t const& InRowSize() const { return inRowSize; } //! Modify the row size of the input. size_t& InRowSize() { return inRowSize; } - //! Get the column size of the input. size_t const& InColSize() const { return inColSize; } //! Modify the column size of the input. size_t& InColSize() { return inColSize; } - //! Get the row size of the output. size_t const& OutRowSize() const { return outRowSize; } //! Modify the row size of the output. size_t& OutRowSize() { return outRowSize; } - //! Get the column size of the output. size_t const& OutColSize() const { return outColSize; } //! Modify the column size of the output. size_t& OutColSize() { return outColSize; } - //! Get the depth of the input. size_t const& InDepth() const { return depth; } //! Modify the depth of the input. size_t& InDepth() { return depth; } - /** * Serialize the layer. */ diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index d90fdc2c5e..7a18a3d89c 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -33,10 +33,10 @@ NearestInterpolationType::NearestInterpolationType(): template NearestInterpolationType:: NearestInterpolationType(const size_t inRowSize, - const size_t inColSize, - const size_t outRowSize, - const size_t outColSize, - const size_t depth) : + const size_t inColSize, + const size_t outRowSize, + const size_t outColSize, + const size_t depth) : Layer(), inRowSize(inRowSize), inColSize(inColSize), From 2f62b3f4ef347558a860128b26a099a5012cc704 Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Sat, 22 Jun 2024 01:43:36 +0530 Subject: [PATCH 031/212] Added sse gain to decision tree. --- .../methods/decision_tree/decision_tree.hpp | 1 + .../sse_gain.hpp} | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) rename src/mlpack/methods/{xgboost/loss_functions/sse_loss.hpp => decision_tree/sse_gain.hpp} (84%) diff --git a/src/mlpack/methods/decision_tree/decision_tree.hpp b/src/mlpack/methods/decision_tree/decision_tree.hpp index af169822b5..44b6ccd475 100644 --- a/src/mlpack/methods/decision_tree/decision_tree.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree.hpp @@ -19,6 +19,7 @@ #include "information_gain.hpp" #include "mad_gain.hpp" #include "mse_gain.hpp" +#include "sse_gain.hpp" #include "best_binary_numeric_split.hpp" #include "random_binary_numeric_split.hpp" diff --git a/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp b/src/mlpack/methods/decision_tree/sse_gain.hpp similarity index 84% rename from src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp rename to src/mlpack/methods/decision_tree/sse_gain.hpp index 1029fa06af..663fdd6433 100644 --- a/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp +++ b/src/mlpack/methods/decision_tree/sse_gain.hpp @@ -1,8 +1,8 @@ /** - * @file methods/xgboost/loss_functions/sse_loss.hpp + * @file methods/decision_tree/sse_gain.hpp * @author Rishabh Garg * - * The sum of squared error loss class, which is a loss funtion for gradient + * The sum of squared error loss class, which is a loss function for gradient * xgboost based decision trees. * * mlpack is free software; you may redistribute it and/or modify it under the @@ -25,13 +25,13 @@ namespace mlpack { * * Loss = 1 / 2 * (Observed - Predicted)^2 */ -class SSELoss +class SSEGain { public: // Default constructor---No regularization. - SSELoss() : alpha(0), lambda(0) { /* Nothing to do. */} + SSEGain() : alpha(0), lambda(0) { /* Nothing to do. */} - SSELoss(const double alpha, const double lambda): + SSEGain(const double alpha, const double lambda): alpha(alpha), lambda(lambda) { // Nothing to do. @@ -66,8 +66,15 @@ class SSELoss * @param begin The begin index to calculate gain. * @param end The end index to calculate gain. */ - double Evaluate(const size_t begin, const size_t end) + template + double Evaluate(const MatType& input, + const WeightVecType& /* weights */, + const size_t begin, + const size_t end) { + gradients = (input.row(1) - input.row(0)).t(); + hessians = arma::vec(input.n_cols, arma::fill::ones); + return std::pow(ApplyL1(accu(gradients.subvec(begin, end))), 2) / (accu(hessians.subvec(begin, end)) + lambda); } From c37bf9dc82dbd5f71aef35ff39fce0329a2b3b6b Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Sat, 22 Jun 2024 02:00:55 +0530 Subject: [PATCH 032/212] organised helper functions into dirs in decision tree dir. --- .../methods/decision_tree/decision_tree.hpp | 23 +++++++++---------- .../decision_tree/decision_tree_regressor.hpp | 14 ++++++----- .../{ => gain_functions}/gini_gain.hpp | 2 +- .../{ => gain_functions}/information_gain.hpp | 2 +- .../{ => gain_functions}/mad_gain.hpp | 4 ++-- .../{ => gain_functions}/mse_gain.hpp | 4 ++-- .../{ => gain_functions}/sse_gain.hpp | 2 +- .../all_dimension_select.hpp | 2 +- .../multiple_random_dimension_select.hpp | 2 +- .../random_dimension_select.hpp | 2 +- .../all_categorical_split.hpp | 2 +- .../all_categorical_split_impl.hpp | 2 +- .../best_binary_numeric_split.hpp | 4 ++-- .../best_binary_numeric_split_impl.hpp | 2 +- .../random_binary_numeric_split.hpp | 2 +- .../random_binary_numeric_split_impl.hpp | 2 +- 16 files changed, 36 insertions(+), 35 deletions(-) rename src/mlpack/methods/decision_tree/{ => gain_functions}/gini_gain.hpp (99%) rename src/mlpack/methods/decision_tree/{ => gain_functions}/information_gain.hpp (98%) rename src/mlpack/methods/decision_tree/{ => gain_functions}/mad_gain.hpp (97%) rename src/mlpack/methods/decision_tree/{ => gain_functions}/mse_gain.hpp (98%) rename src/mlpack/methods/decision_tree/{ => gain_functions}/sse_gain.hpp (98%) rename src/mlpack/methods/decision_tree/{ => select_functions}/all_dimension_select.hpp (95%) rename src/mlpack/methods/decision_tree/{ => select_functions}/multiple_random_dimension_select.hpp (96%) rename src/mlpack/methods/decision_tree/{ => select_functions}/random_dimension_select.hpp (95%) rename src/mlpack/methods/decision_tree/{ => split_functions}/all_categorical_split.hpp (98%) rename src/mlpack/methods/decision_tree/{ => split_functions}/all_categorical_split_impl.hpp (98%) rename src/mlpack/methods/decision_tree/{ => split_functions}/best_binary_numeric_split.hpp (98%) rename src/mlpack/methods/decision_tree/{ => split_functions}/best_binary_numeric_split_impl.hpp (99%) rename src/mlpack/methods/decision_tree/{ => split_functions}/random_binary_numeric_split.hpp (98%) rename src/mlpack/methods/decision_tree/{ => split_functions}/random_binary_numeric_split_impl.hpp (98%) diff --git a/src/mlpack/methods/decision_tree/decision_tree.hpp b/src/mlpack/methods/decision_tree/decision_tree.hpp index 44b6ccd475..4b92938681 100644 --- a/src/mlpack/methods/decision_tree/decision_tree.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree.hpp @@ -15,20 +15,19 @@ #include -#include "gini_gain.hpp" -#include "information_gain.hpp" -#include "mad_gain.hpp" -#include "mse_gain.hpp" -#include "sse_gain.hpp" +#include "gain_functions/gini_gain.hpp" +#include "gain_functions/information_gain.hpp" +#include "gain_functions/mad_gain.hpp" +#include "gain_functions/mse_gain.hpp" +#include "gain_functions/sse_gain.hpp" -#include "best_binary_numeric_split.hpp" -#include "random_binary_numeric_split.hpp" +#include "split_functions/best_binary_numeric_split.hpp" +#include "split_functions/random_binary_numeric_split.hpp" +#include "split_functions/all_categorical_split.hpp" -#include "all_categorical_split.hpp" - -#include "all_dimension_select.hpp" -#include "random_dimension_select.hpp" -#include "multiple_random_dimension_select.hpp" +#include "select_functions/all_dimension_select.hpp" +#include "select_functions/random_dimension_select.hpp" +#include "select_functions/multiple_random_dimension_select.hpp" namespace mlpack { diff --git a/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp b/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp index aaa9f74f5b..4dd9732f5f 100644 --- a/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp @@ -15,12 +15,14 @@ #include -#include "mad_gain.hpp" -#include "mse_gain.hpp" -#include "best_binary_numeric_split.hpp" -#include "all_categorical_split.hpp" -#include "random_binary_numeric_split.hpp" -#include "all_dimension_select.hpp" +#include "gain_functions/mad_gain.hpp" +#include "gain_functions/mse_gain.hpp" + +#include "split_functions/best_binary_numeric_split.hpp" +#include "split_functions/all_categorical_split.hpp" +#include "split_functions/random_binary_numeric_split.hpp" + +#include "select_functions/all_dimension_select.hpp" namespace mlpack { diff --git a/src/mlpack/methods/decision_tree/gini_gain.hpp b/src/mlpack/methods/decision_tree/gain_functions/gini_gain.hpp similarity index 99% rename from src/mlpack/methods/decision_tree/gini_gain.hpp rename to src/mlpack/methods/decision_tree/gain_functions/gini_gain.hpp index ea7d4a5407..e52a23d3af 100644 --- a/src/mlpack/methods/decision_tree/gini_gain.hpp +++ b/src/mlpack/methods/decision_tree/gain_functions/gini_gain.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/gini_gain.hpp + * @file methods/decision_tree/gain_functions/gini_gain.hpp * @author Ryan Curtin * * The GiniGain class, which is a fitness function (FitnessFunction) for diff --git a/src/mlpack/methods/decision_tree/information_gain.hpp b/src/mlpack/methods/decision_tree/gain_functions/information_gain.hpp similarity index 98% rename from src/mlpack/methods/decision_tree/information_gain.hpp rename to src/mlpack/methods/decision_tree/gain_functions/information_gain.hpp index 7cf0f1158e..a768886114 100644 --- a/src/mlpack/methods/decision_tree/information_gain.hpp +++ b/src/mlpack/methods/decision_tree/gain_functions/information_gain.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/information_gain.hpp + * @file methods/decision_tree/gain_functions/information_gain.hpp * @author Ryan Curtin * * An implementation of information gain, which can be used in place of Gini diff --git a/src/mlpack/methods/decision_tree/mad_gain.hpp b/src/mlpack/methods/decision_tree/gain_functions/mad_gain.hpp similarity index 97% rename from src/mlpack/methods/decision_tree/mad_gain.hpp rename to src/mlpack/methods/decision_tree/gain_functions/mad_gain.hpp index 742700fb34..bcf1275e57 100644 --- a/src/mlpack/methods/decision_tree/mad_gain.hpp +++ b/src/mlpack/methods/decision_tree/gain_functions/mad_gain.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/mad_gain.hpp + * @file methods/decision_tree/gain_functions/mad_gain.hpp * @author Rishabh Garg * * The mean absolute deviation gain class, a fitness function for regression @@ -15,7 +15,7 @@ n. #define MLPACK_METHODS_DECISION_TREE_MAD_GAIN_HPP #include -#include "utils.hpp" +#include "mlpack/methods/decision_tree/utils.hpp" namespace mlpack { diff --git a/src/mlpack/methods/decision_tree/mse_gain.hpp b/src/mlpack/methods/decision_tree/gain_functions/mse_gain.hpp similarity index 98% rename from src/mlpack/methods/decision_tree/mse_gain.hpp rename to src/mlpack/methods/decision_tree/gain_functions/mse_gain.hpp index 8e64a97a01..f81ffe0781 100644 --- a/src/mlpack/methods/decision_tree/mse_gain.hpp +++ b/src/mlpack/methods/decision_tree/gain_functions/mse_gain.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/mse_gain.hpp + * @file methods/decision_tree/gain_functions/mse_gain.hpp * @author Rishabh Garg * * The mean squared error gain class, which is a fitness funtion for @@ -14,7 +14,7 @@ #define MLPACK_METHODS_DECISION_TREE_MSE_GAIN_HPP #include -#include "utils.hpp" +#include "mlpack/methods/decision_tree/utils.hpp" namespace mlpack { diff --git a/src/mlpack/methods/decision_tree/sse_gain.hpp b/src/mlpack/methods/decision_tree/gain_functions/sse_gain.hpp similarity index 98% rename from src/mlpack/methods/decision_tree/sse_gain.hpp rename to src/mlpack/methods/decision_tree/gain_functions/sse_gain.hpp index 663fdd6433..c0351fca7f 100644 --- a/src/mlpack/methods/decision_tree/sse_gain.hpp +++ b/src/mlpack/methods/decision_tree/gain_functions/sse_gain.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/sse_gain.hpp + * @file methods/decision_tree/gain_functions/sse_gain.hpp * @author Rishabh Garg * * The sum of squared error loss class, which is a loss function for gradient diff --git a/src/mlpack/methods/decision_tree/all_dimension_select.hpp b/src/mlpack/methods/decision_tree/select_functions/all_dimension_select.hpp similarity index 95% rename from src/mlpack/methods/decision_tree/all_dimension_select.hpp rename to src/mlpack/methods/decision_tree/select_functions/all_dimension_select.hpp index 332a439fd8..5c59a6f60c 100644 --- a/src/mlpack/methods/decision_tree/all_dimension_select.hpp +++ b/src/mlpack/methods/decision_tree/select_functions/all_dimension_select.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/all_dimension_select.hpp + * @file methods/decision_tree/select_functions/all_dimension_select.hpp * @author Ryan Curtin * * Selects all dimensions for a split. diff --git a/src/mlpack/methods/decision_tree/multiple_random_dimension_select.hpp b/src/mlpack/methods/decision_tree/select_functions/multiple_random_dimension_select.hpp similarity index 96% rename from src/mlpack/methods/decision_tree/multiple_random_dimension_select.hpp rename to src/mlpack/methods/decision_tree/select_functions/multiple_random_dimension_select.hpp index 6ce277d258..d0c3fff36a 100644 --- a/src/mlpack/methods/decision_tree/multiple_random_dimension_select.hpp +++ b/src/mlpack/methods/decision_tree/select_functions/multiple_random_dimension_select.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/multiple_random_dimension_select.hpp + * @file methods/decision_tree/select_functions/multiple_random_dimension_select.hpp * @author Ryan Curtin * * Select a number of random dimensions to pick from. diff --git a/src/mlpack/methods/decision_tree/random_dimension_select.hpp b/src/mlpack/methods/decision_tree/select_functions/random_dimension_select.hpp similarity index 95% rename from src/mlpack/methods/decision_tree/random_dimension_select.hpp rename to src/mlpack/methods/decision_tree/select_functions/random_dimension_select.hpp index fdf024a70a..03a32f5fc2 100644 --- a/src/mlpack/methods/decision_tree/random_dimension_select.hpp +++ b/src/mlpack/methods/decision_tree/select_functions/random_dimension_select.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/random_dimension_select.hpp + * @file methods/decision_tree/select_functions/random_dimension_select.hpp * @author Ryan Curtin * * Selects one single random dimension to split on. diff --git a/src/mlpack/methods/decision_tree/all_categorical_split.hpp b/src/mlpack/methods/decision_tree/split_functions/all_categorical_split.hpp similarity index 98% rename from src/mlpack/methods/decision_tree/all_categorical_split.hpp rename to src/mlpack/methods/decision_tree/split_functions/all_categorical_split.hpp index 9d432b732c..76ec876d95 100644 --- a/src/mlpack/methods/decision_tree/all_categorical_split.hpp +++ b/src/mlpack/methods/decision_tree/split_functions/all_categorical_split.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/all_categorical_split.hpp + * @file methods/decision_tree/split_functions/all_categorical_split.hpp * @author Ryan Curtin * * This file defines a tree splitter that split a categorical feature into all diff --git a/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp b/src/mlpack/methods/decision_tree/split_functions/all_categorical_split_impl.hpp similarity index 98% rename from src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp rename to src/mlpack/methods/decision_tree/split_functions/all_categorical_split_impl.hpp index 3ee94f1818..abb34c05f2 100644 --- a/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp +++ b/src/mlpack/methods/decision_tree/split_functions/all_categorical_split_impl.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/all_categorical_split_impl.hpp + * @file methods/decision_tree/split_functions/all_categorical_split_impl.hpp * @author Ryan Curtin * * Implementation of the AllCategoricalSplit categorical split class. diff --git a/src/mlpack/methods/decision_tree/best_binary_numeric_split.hpp b/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split.hpp similarity index 98% rename from src/mlpack/methods/decision_tree/best_binary_numeric_split.hpp rename to src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split.hpp index 34d397c829..6bc5b101f0 100644 --- a/src/mlpack/methods/decision_tree/best_binary_numeric_split.hpp +++ b/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/best_binary_numeric_split.hpp + * @file methods/decision_tree/split_functions/best_binary_numeric_split.hpp * @author Ryan Curtin * * A tree splitter that finds the best binary numeric split. @@ -13,7 +13,7 @@ #define MLPACK_METHODS_DECISION_TREE_BEST_BINARY_NUMERIC_SPLIT_HPP #include -#include "mse_gain.hpp" +#include "gain_functions/mse_gain.hpp" #include diff --git a/src/mlpack/methods/decision_tree/best_binary_numeric_split_impl.hpp b/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split_impl.hpp similarity index 99% rename from src/mlpack/methods/decision_tree/best_binary_numeric_split_impl.hpp rename to src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split_impl.hpp index e8c8647206..1e6fc50b74 100644 --- a/src/mlpack/methods/decision_tree/best_binary_numeric_split_impl.hpp +++ b/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split_impl.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/best_binary_numeric_split_impl.hpp + * @file methods/decision_tree/split_functions/best_binary_numeric_split_impl.hpp * @author Ryan Curtin * * Implementation of strategy that finds the best binary numeric split. diff --git a/src/mlpack/methods/decision_tree/random_binary_numeric_split.hpp b/src/mlpack/methods/decision_tree/split_functions/random_binary_numeric_split.hpp similarity index 98% rename from src/mlpack/methods/decision_tree/random_binary_numeric_split.hpp rename to src/mlpack/methods/decision_tree/split_functions/random_binary_numeric_split.hpp index a92d9d6eef..7173c7ba17 100644 --- a/src/mlpack/methods/decision_tree/random_binary_numeric_split.hpp +++ b/src/mlpack/methods/decision_tree/split_functions/random_binary_numeric_split.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/random_binary_numeric_split.hpp + * @file methods/decision_tree/split_functions/random_binary_numeric_split.hpp * @author Rishabh Garg * * A tree splitter that finds a random binary numeric split. diff --git a/src/mlpack/methods/decision_tree/random_binary_numeric_split_impl.hpp b/src/mlpack/methods/decision_tree/split_functions/random_binary_numeric_split_impl.hpp similarity index 98% rename from src/mlpack/methods/decision_tree/random_binary_numeric_split_impl.hpp rename to src/mlpack/methods/decision_tree/split_functions/random_binary_numeric_split_impl.hpp index 9139214248..7447b84e7a 100644 --- a/src/mlpack/methods/decision_tree/random_binary_numeric_split_impl.hpp +++ b/src/mlpack/methods/decision_tree/split_functions/random_binary_numeric_split_impl.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/random_binary_numeric_split_impl.hpp + * @file methods/decision_tree/split_functions/random_binary_numeric_split_impl.hpp * @author Rishabh Garg * * Implementation of strategy that finds the random binary numeric split. From ad569ee036c1c38a4ae8d6f2bb659171a9122429 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Fri, 21 Jun 2024 22:17:40 +0100 Subject: [PATCH 033/212] use MakeAlias instead of memptr --- .../methods/ann/layer/nearest_interpolation_impl.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index 7a18a3d89c..8315b0147b 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -126,10 +126,11 @@ void NearestInterpolationType::Forward( assert(inRowSize >= 2); assert(inColSize >= 2); - arma::cube inputAsCube(const_cast(input).memptr(), - inRowSize, inColSize, depth * batchSize, false, false); - arma::cube outputAsCube(output.memptr(), outRowSize, outColSize, - depth * batchSize, false, true); + arma::cube inputAsCube; + arma::cube outputAsCube; + + MakeAlias(inputAsCube, input, inRowSize, inColSize, depth*batchSize, 0, false); + MakeAlias(outputAsCube, output, outRowSize, outColSize, depth*batchSize, 0, true); double scaleRow = (double) inRowSize / (double) outRowSize; double scaleCol = (double) inColSize / (double) outColSize; From 29e7edd25c31d591180c974f9a3740719a741dc3 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Fri, 21 Jun 2024 22:44:30 +0100 Subject: [PATCH 034/212] update backward to use MakeAlias instead of memptr --- .../methods/ann/layer/nearest_interpolation_impl.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index 8315b0147b..c994012750 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -171,10 +171,11 @@ void NearestInterpolationType::Backward( assert(outRowSize >= 2); assert(outColSize >= 2); - arma::cube outputAsCube(output.memptr(), inRowSize, inColSize, - depth * batchSize, false, true); - arma::cube gradientAsCube(((MatType&) gradient).memptr(), outRowSize, - outColSize, depth * batchSize, false, false); + arma::cube outputAsCube; + arma::cube gradientAsCube; + + MakeAlias(outputAsCube, output, inRowSize, inColSize, depth*batchSize, 0, true); + MakeAlias(gradientAsCube, gradient, outRowSize, outColSize, depth*batchSize, 0, false); double scaleRow = (double)(inRowSize) / outRowSize; double scaleCol = (double)(inColSize) / outColSize; From 2733d7708e0109080280d3d630d28f4e163e3ace Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Fri, 21 Jun 2024 23:29:24 +0100 Subject: [PATCH 035/212] added tests for nearest interpolation --- .../ann/layer/nearest_interpolation_test.cpp | 83 +++++++++++++++++++ src/mlpack/tests/ann/layer_test.cpp | 1 + .../tests/ann/not_adapted/ann_layer_test.cpp | 64 -------------- 3 files changed, 84 insertions(+), 64 deletions(-) create mode 100644 src/mlpack/tests/ann/layer/nearest_interpolation_test.cpp diff --git a/src/mlpack/tests/ann/layer/nearest_interpolation_test.cpp b/src/mlpack/tests/ann/layer/nearest_interpolation_test.cpp new file mode 100644 index 0000000000..f9ddd67581 --- /dev/null +++ b/src/mlpack/tests/ann/layer/nearest_interpolation_test.cpp @@ -0,0 +1,83 @@ +/** + * @file tests/ann/layer/add.cpp + * @author Ryan Curtin + * + * Tests the nearest interpolation layer + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include +#include + +#include "../../test_catch_tools.hpp" +#include "../../catch.hpp" +#include "../../serialization.hpp" +#include "../ann_test_tools.hpp" + +using namespace mlpack; + +/** + * Simple test for the NearestInterpolation layer + */ +TEST_CASE("SimpleNearestInterpolationLayerTest", "[ANNLayerTest]") +{ + // Tested output against torch.nn.Upsample(mode="nearest"). + arma::mat input, output, unzoomedOutput, expectedOutput; + size_t inRowSize = 2; + size_t inColSize = 2; + size_t outRowSize = 5; + size_t outColSize = 7; + size_t depth = 1; + input.zeros(inRowSize * inColSize * depth, 1); + input[0] = 1.0; + input[1] = 3.0; + input[2] = 2.0; + input[3] = 4.0; + NearestInterpolation layer(inRowSize, inColSize, outRowSize, + outColSize, depth); + + expectedOutput << 1.0000 << 1.0000 << 1.0000 << 1.0000 << 2.0000 + << 2.0000 << 2.0000 << arma::endr + << 1.0000 << 1.0000 << 1.0000 << 1.0000 << 2.0000 + << 2.0000 << 2.0000 << arma::endr + << 1.0000 << 1.0000 << 1.0000 << 1.0000 << 2.0000 + << 2.0000 << 2.0000 << arma::endr + << 3.0000 << 3.0000 << 3.0000 << 3.0000 << 4.0000 + << 4.0000 << 4.0000 << arma::endr + << 3.0000 << 3.0000 << 3.0000 << 3.0000 << 4.0000 + << 4.0000 << 4.0000 << arma::endr; + expectedOutput.reshape(35, 1); + + layer.Forward(input, output); + CheckMatrices(output - expectedOutput, + arma::zeros(output.n_rows), 1e-4); + + expectedOutput.clear(); + expectedOutput << 12.0000 << 18.0000 << arma::endr + << 24.0000 << 24.0000 << arma::endr; + expectedOutput.reshape(4, 1); + layer.Backward(output, output, unzoomedOutput); + CheckMatrices(unzoomedOutput - expectedOutput, + arma::zeros(input.n_rows), 1e-4); + + arma::mat input1, output1, unzoomedOutput1, expectedOutput1; + inRowSize = 2; + inColSize = 3; + outRowSize = 17; + outColSize = 23; + input1 << 1 << 2 << 3 << arma::endr + << 4 << 5 << 6 << arma::endr; + input1.reshape(6, 1); + NearestInterpolation layer1(inRowSize, inColSize, outRowSize, + outColSize, depth); + + layer1.Forward(input1, output1); + layer1.Backward(output1, output1, unzoomedOutput1); + + REQUIRE(accu(output1) - 1317.00 == Approx(0.0).margin(1e-05)); + REQUIRE(accu(unzoomedOutput1) - 1317.00 == + Approx(0.0).margin(1e-05)); +} diff --git a/src/mlpack/tests/ann/layer_test.cpp b/src/mlpack/tests/ann/layer_test.cpp index 372563f433..ac3c1a3f4e 100644 --- a/src/mlpack/tests/ann/layer_test.cpp +++ b/src/mlpack/tests/ann/layer_test.cpp @@ -39,6 +39,7 @@ #include "layer/log_softmax.cpp" #include "layer/max_pooling.cpp" #include "layer/mean_pooling.cpp" +#include "layer/nearest_interpolation_test.cpp" #include "layer/padding.cpp" #include "layer/parametric_relu.cpp" #include "layer/relu6.cpp" diff --git a/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp b/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp index 4ceac2ffbd..fd3a81ee00 100644 --- a/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp +++ b/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp @@ -1875,70 +1875,6 @@ TEST_CASE("LookupLayerParametersTest", "[ANNLayerTest]") } */ -/** - * Simple test for the NearestInterpolation layer - * -TEST_CASE("SimpleNearestInterpolationLayerTest", "[ANNLayerTest]") -{ - // Tested output against torch.nn.Upsample(mode="nearest"). - arma::mat input, output, unzoomedOutput, expectedOutput; - size_t inRowSize = 2; - size_t inColSize = 2; - size_t outRowSize = 5; - size_t outColSize = 7; - size_t depth = 1; - input.zeros(inRowSize * inColSize * depth, 1); - input[0] = 1.0; - input[1] = 3.0; - input[2] = 2.0; - input[3] = 4.0; - NearestInterpolation<> layer(inRowSize, inColSize, outRowSize, - outColSize, depth); - - expectedOutput << 1.0000 << 1.0000 << 1.0000 << 1.0000 << 2.0000 - << 2.0000 << 2.0000 << arma::endr - << 1.0000 << 1.0000 << 1.0000 << 1.0000 << 2.0000 - << 2.0000 << 2.0000 << arma::endr - << 1.0000 << 1.0000 << 1.0000 << 1.0000 << 2.0000 - << 2.0000 << 2.0000 << arma::endr - << 3.0000 << 3.0000 << 3.0000 << 3.0000 << 4.0000 - << 4.0000 << 4.0000 << arma::endr - << 3.0000 << 3.0000 << 3.0000 << 3.0000 << 4.0000 - << 4.0000 << 4.0000 << arma::endr; - expectedOutput.reshape(35, 1); - - layer.Forward(input, output); - CheckMatrices(output - expectedOutput, - arma::zeros(output.n_rows), 1e-4); - - expectedOutput.clear(); - expectedOutput << 12.0000 << 18.0000 << arma::endr - << 24.0000 << 24.0000 << arma::endr; - expectedOutput.reshape(4, 1); - layer.Backward(output, output, unzoomedOutput); - CheckMatrices(unzoomedOutput - expectedOutput, - arma::zeros(input.n_rows), 1e-4); - - arma::mat input1, output1, unzoomedOutput1, expectedOutput1; - inRowSize = 2; - inColSize = 3; - outRowSize = 17; - outColSize = 23; - input1 << 1 << 2 << 3 << arma::endr - << 4 << 5 << 6 << arma::endr; - input1.reshape(6, 1); - NearestInterpolation<> layer1(inRowSize, inColSize, outRowSize, - outColSize, depth); - - layer1.Forward(input1, output1); - layer1.Backward(output1, output1, unzoomedOutput1); - - REQUIRE(accu(output1) - 1317.00 == Approx(0.0).margin(1e-05)); - REQUIRE(accu(unzoomedOutput1) - 1317.00 == - Approx(0.0).margin(1e-05)); -} -*/ - /* * Simple test for the BilinearInterpolation layer * From 2fa2e824bac21dbb5e5527f6596471aac93b954b Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Sat, 22 Jun 2024 17:31:31 +0530 Subject: [PATCH 036/212] minor fix path --- .../decision_tree/split_functions/best_binary_numeric_split.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split.hpp b/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split.hpp index 6bc5b101f0..438a31fbc0 100644 --- a/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split.hpp +++ b/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split.hpp @@ -13,7 +13,7 @@ #define MLPACK_METHODS_DECISION_TREE_BEST_BINARY_NUMERIC_SPLIT_HPP #include -#include "gain_functions/mse_gain.hpp" +#include #include From 5eba73c0673beb4f053f1f63b3a97ffcd0ba059f Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Sat, 22 Jun 2024 18:55:00 +0530 Subject: [PATCH 037/212] added convenience def --- src/mlpack/methods/decision_tree/decision_tree.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/mlpack/methods/decision_tree/decision_tree.hpp b/src/mlpack/methods/decision_tree/decision_tree.hpp index 4b92938681..632767298b 100644 --- a/src/mlpack/methods/decision_tree/decision_tree.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree.hpp @@ -611,6 +611,15 @@ typedef DecisionTree ID3DecisionStump; + +/** + * Convenience typedef for XGBoost trees. + */ +typedef DecisionTree XGBTree; } // namespace mlpack // Include implementation. From 0cf8d5e2ce1936f681f030179c2c3921db8a58c3 Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Sat, 22 Jun 2024 21:28:34 +0530 Subject: [PATCH 038/212] started work on log_gain --- .../decision_tree/gain_functions/log_gain.cpp | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/mlpack/methods/decision_tree/gain_functions/log_gain.cpp diff --git a/src/mlpack/methods/decision_tree/gain_functions/log_gain.cpp b/src/mlpack/methods/decision_tree/gain_functions/log_gain.cpp new file mode 100644 index 0000000000..69d47fac22 --- /dev/null +++ b/src/mlpack/methods/decision_tree/gain_functions/log_gain.cpp @@ -0,0 +1,81 @@ +/** + * @file methods/decision_tree/gain_functions/log_gain.hpp + * @author Abhimanyu Dayal + * + * The logistic gain class, which is a gain function for xgboost. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_METHODS_XGBOOST_LOSS_FUNCTIONS_LOG_LOSS_HPP +#define MLPACK_METHODS_XGBOOST_LOSS_FUNCTIONS_LOG_LOSS_HPP + +#include + +namespace mlpack { +/** + * Logistic loss, also known as log loss or cross-entropy loss, is a loss + * function used in logistic regression to measure the difference between + * the predicted probability of an event and the actual outcome. + * + * Log Loss = - (1 / N) * Σ [y_i * log(p_i) + (1 - y_i) * log(1 - p_i)] + */ +class LogLoss +{ + public: + // Default constructor---No regularization. + LogLoss() : alpha(0), lambda(0) { /* Nothing to do. */} + + LogLoss(const double alpha, const double lambda): + alpha(alpha), lambda(lambda) + { + // Nothing to do. + } + + /** + * Returns the initial prediction for gradient boosting. + */ + template + typename VecType::elem_type InitialPrediction(const VecType& values) + { + // Sanity check for empty vector. + if (values.n_elem == 0) + return 0; + + // Return the log-odds of the mean of the values. + double mean = accu(values) / (typename VecType::elem_type) values.n_elem; + return std::log(mean / (1 - mean)); + } + + + private: + //! The L1 regularization parameter. + const double alpha; + //! The L2 regularization parameter. + const double lambda; + //! First order gradients. + arma::vec gradients; + //! Second order gradients (hessians). + arma::vec hessians; + + //! Applies the L1 regularization. + double ApplyL1(const double sumGradients) + { + if (sumGradients > alpha) + { + return sumGradients - alpha; + } + else if (sumGradients < - alpha) + { + return sumGradients + alpha; + } + + return 0; + } + +}; +} // namespace mlpack + +#endif From 95f1e5d101906707367533cf24b46d2c69ed3d39 Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Sun, 23 Jun 2024 19:39:15 +0530 Subject: [PATCH 039/212] added convenience include files --- .../methods/decision_tree/decision_tree.hpp | 16 +++------------- .../decision_tree/decision_tree_regressor.hpp | 11 +++-------- .../gain_functions/gain_functions.hpp | 6 ++++++ .../{log_gain.cpp => log_gain.hpp} | 0 .../select_functions/select_functions.hpp | 3 +++ .../split_functions/split_functions.hpp | 3 +++ 6 files changed, 18 insertions(+), 21 deletions(-) create mode 100644 src/mlpack/methods/decision_tree/gain_functions/gain_functions.hpp rename src/mlpack/methods/decision_tree/gain_functions/{log_gain.cpp => log_gain.hpp} (100%) create mode 100644 src/mlpack/methods/decision_tree/select_functions/select_functions.hpp create mode 100644 src/mlpack/methods/decision_tree/split_functions/split_functions.hpp diff --git a/src/mlpack/methods/decision_tree/decision_tree.hpp b/src/mlpack/methods/decision_tree/decision_tree.hpp index 632767298b..73d557ebbb 100644 --- a/src/mlpack/methods/decision_tree/decision_tree.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree.hpp @@ -15,19 +15,9 @@ #include -#include "gain_functions/gini_gain.hpp" -#include "gain_functions/information_gain.hpp" -#include "gain_functions/mad_gain.hpp" -#include "gain_functions/mse_gain.hpp" -#include "gain_functions/sse_gain.hpp" - -#include "split_functions/best_binary_numeric_split.hpp" -#include "split_functions/random_binary_numeric_split.hpp" -#include "split_functions/all_categorical_split.hpp" - -#include "select_functions/all_dimension_select.hpp" -#include "select_functions/random_dimension_select.hpp" -#include "select_functions/multiple_random_dimension_select.hpp" +#include "gain_functions/gain_functions.hpp" +#include "split_functions/split_functions.hpp" +#include "select_functions/select_functions.hpp" namespace mlpack { diff --git a/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp b/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp index 4dd9732f5f..1c77050f90 100644 --- a/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp @@ -15,14 +15,9 @@ #include -#include "gain_functions/mad_gain.hpp" -#include "gain_functions/mse_gain.hpp" - -#include "split_functions/best_binary_numeric_split.hpp" -#include "split_functions/all_categorical_split.hpp" -#include "split_functions/random_binary_numeric_split.hpp" - -#include "select_functions/all_dimension_select.hpp" +#include "gain_functions/gain_functions.hpp" +#include "split_functions/split_functions.hpp" +#include "select_functions/select_functions.hpp" namespace mlpack { diff --git a/src/mlpack/methods/decision_tree/gain_functions/gain_functions.hpp b/src/mlpack/methods/decision_tree/gain_functions/gain_functions.hpp new file mode 100644 index 0000000000..f94e653de9 --- /dev/null +++ b/src/mlpack/methods/decision_tree/gain_functions/gain_functions.hpp @@ -0,0 +1,6 @@ +#include "gini_gain.hpp" +#include "information_gain.hpp" +#include "log_gain.hpp" +#include "mad_gain.hpp" +#include "mse_gain.hpp" +#include "sse_gain.hpp" \ No newline at end of file diff --git a/src/mlpack/methods/decision_tree/gain_functions/log_gain.cpp b/src/mlpack/methods/decision_tree/gain_functions/log_gain.hpp similarity index 100% rename from src/mlpack/methods/decision_tree/gain_functions/log_gain.cpp rename to src/mlpack/methods/decision_tree/gain_functions/log_gain.hpp diff --git a/src/mlpack/methods/decision_tree/select_functions/select_functions.hpp b/src/mlpack/methods/decision_tree/select_functions/select_functions.hpp new file mode 100644 index 0000000000..da857189f1 --- /dev/null +++ b/src/mlpack/methods/decision_tree/select_functions/select_functions.hpp @@ -0,0 +1,3 @@ +#include "all_dimension_select.hpp" +#include "multiple_random_dimension_select.hpp" +#include "random_dimension_select.hpp" \ No newline at end of file diff --git a/src/mlpack/methods/decision_tree/split_functions/split_functions.hpp b/src/mlpack/methods/decision_tree/split_functions/split_functions.hpp new file mode 100644 index 0000000000..067be112ec --- /dev/null +++ b/src/mlpack/methods/decision_tree/split_functions/split_functions.hpp @@ -0,0 +1,3 @@ +#include "all_categorical_split.hpp" +#include "best_binary_numeric_split.hpp" +#include "random_binary_numeric_split.hpp" \ No newline at end of file From ca493b5ab48d9e0b65dd979519374b2430097458 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Tue, 25 Jun 2024 17:53:55 +0100 Subject: [PATCH 040/212] fixed comments and made some style changes --- .../methods/ann/layer/nearest_interpolation.hpp | 11 +++++------ .../ann/layer/nearest_interpolation_impl.hpp | 14 ++++++++------ .../tests/ann/layer/nearest_interpolation_test.cpp | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp index 8a4af66937..717b214cdb 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp @@ -39,20 +39,19 @@ class NearestInterpolationType : public Layer const size_t outRowSize, const size_t outColSize, const size_t depth); - - NearestInterpolationType* Clone() const { + NearestInterpolationType* Clone() const { return new NearestInterpolationType(*this); } virtual ~NearestInterpolationType() { } - //! Copy the given ConcatenateType layer. + //! Copy the given NearestInterpolationType layer. NearestInterpolationType(const NearestInterpolationType& other); - //! Take ownership of the given ConcatenateType layer. + //! Take ownership of the given NearestInterpolationType layer. NearestInterpolationType(NearestInterpolationType&& other); - //! Copy the given ConcatenateType layer. + //! Copy the given NearestInterpolationType layer. NearestInterpolationType& operator=(const NearestInterpolationType& other); - //! Take ownership of the given ConcatenateType layer. + //! Take ownership of the given NearestInterpolationType layer. NearestInterpolationType& operator=(NearestInterpolationType&& other); /** diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index c994012750..fa898c87a9 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -81,7 +81,8 @@ NearestInterpolationType& NearestInterpolationType:: operator=(const NearestInterpolationType& other) { - if (&other != this) { + if (&other != this) + { Layer::operator=(other); inRowSize = other.inRowSize; inColSize = other.inColSize; @@ -98,7 +99,8 @@ NearestInterpolationType& NearestInterpolationType:: operator=(NearestInterpolationType&& other) { - if (&other != this) { + if (&other != this) + { Layer::operator=(std::move(other)); inRowSize = std::move(other.inRowSize); inColSize = std::move(other.inColSize); @@ -129,8 +131,8 @@ void NearestInterpolationType::Forward( arma::cube inputAsCube; arma::cube outputAsCube; - MakeAlias(inputAsCube, input, inRowSize, inColSize, depth*batchSize, 0, false); - MakeAlias(outputAsCube, output, outRowSize, outColSize, depth*batchSize, 0, true); + MakeAlias(inputAsCube, input, inRowSize, inColSize, depth * batchSize, 0, false); + MakeAlias(outputAsCube, output, outRowSize, outColSize, depth * batchSize, 0, true); double scaleRow = (double) inRowSize / (double) outRowSize; double scaleCol = (double) inColSize / (double) outColSize; @@ -174,8 +176,8 @@ void NearestInterpolationType::Backward( arma::cube outputAsCube; arma::cube gradientAsCube; - MakeAlias(outputAsCube, output, inRowSize, inColSize, depth*batchSize, 0, true); - MakeAlias(gradientAsCube, gradient, outRowSize, outColSize, depth*batchSize, 0, false); + MakeAlias(outputAsCube, output, inRowSize, inColSize, depth * batchSize, 0, true); + MakeAlias(gradientAsCube, gradient, outRowSize, outColSize, depth * batchSize, 0, false); double scaleRow = (double)(inRowSize) / outRowSize; double scaleCol = (double)(inColSize) / outColSize; diff --git a/src/mlpack/tests/ann/layer/nearest_interpolation_test.cpp b/src/mlpack/tests/ann/layer/nearest_interpolation_test.cpp index f9ddd67581..20ab7c33e2 100644 --- a/src/mlpack/tests/ann/layer/nearest_interpolation_test.cpp +++ b/src/mlpack/tests/ann/layer/nearest_interpolation_test.cpp @@ -37,7 +37,7 @@ TEST_CASE("SimpleNearestInterpolationLayerTest", "[ANNLayerTest]") input[2] = 2.0; input[3] = 4.0; NearestInterpolation layer(inRowSize, inColSize, outRowSize, - outColSize, depth); + outColSize, depth); expectedOutput << 1.0000 << 1.0000 << 1.0000 << 1.0000 << 2.0000 << 2.0000 << 2.0000 << arma::endr @@ -72,7 +72,7 @@ TEST_CASE("SimpleNearestInterpolationLayerTest", "[ANNLayerTest]") << 4 << 5 << 6 << arma::endr; input1.reshape(6, 1); NearestInterpolation layer1(inRowSize, inColSize, outRowSize, - outColSize, depth); + outColSize, depth); layer1.Forward(input1, output1); layer1.Backward(output1, output1, unzoomedOutput1); From e75cc2149c467034ef8a504ad39d5448a4cde7d5 Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Wed, 26 Jun 2024 23:38:31 +0530 Subject: [PATCH 041/212] shifting log stuff to another PR --- .../decision_tree/gain_functions/log_gain.hpp | 81 ------------------- 1 file changed, 81 deletions(-) delete mode 100644 src/mlpack/methods/decision_tree/gain_functions/log_gain.hpp diff --git a/src/mlpack/methods/decision_tree/gain_functions/log_gain.hpp b/src/mlpack/methods/decision_tree/gain_functions/log_gain.hpp deleted file mode 100644 index 69d47fac22..0000000000 --- a/src/mlpack/methods/decision_tree/gain_functions/log_gain.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @file methods/decision_tree/gain_functions/log_gain.hpp - * @author Abhimanyu Dayal - * - * The logistic gain class, which is a gain function for xgboost. - * - * mlpack is free software; you may redistribute it and/or modify it under the - * terms of the 3-clause BSD license. You should have received a copy of the - * 3-clause BSD license along with mlpack. If not, see - * http://www.opensource.org/licenses/BSD-3-Clause for more information. - */ -#ifndef MLPACK_METHODS_XGBOOST_LOSS_FUNCTIONS_LOG_LOSS_HPP -#define MLPACK_METHODS_XGBOOST_LOSS_FUNCTIONS_LOG_LOSS_HPP - -#include - -namespace mlpack { -/** - * Logistic loss, also known as log loss or cross-entropy loss, is a loss - * function used in logistic regression to measure the difference between - * the predicted probability of an event and the actual outcome. - * - * Log Loss = - (1 / N) * Σ [y_i * log(p_i) + (1 - y_i) * log(1 - p_i)] - */ -class LogLoss -{ - public: - // Default constructor---No regularization. - LogLoss() : alpha(0), lambda(0) { /* Nothing to do. */} - - LogLoss(const double alpha, const double lambda): - alpha(alpha), lambda(lambda) - { - // Nothing to do. - } - - /** - * Returns the initial prediction for gradient boosting. - */ - template - typename VecType::elem_type InitialPrediction(const VecType& values) - { - // Sanity check for empty vector. - if (values.n_elem == 0) - return 0; - - // Return the log-odds of the mean of the values. - double mean = accu(values) / (typename VecType::elem_type) values.n_elem; - return std::log(mean / (1 - mean)); - } - - - private: - //! The L1 regularization parameter. - const double alpha; - //! The L2 regularization parameter. - const double lambda; - //! First order gradients. - arma::vec gradients; - //! Second order gradients (hessians). - arma::vec hessians; - - //! Applies the L1 regularization. - double ApplyL1(const double sumGradients) - { - if (sumGradients > alpha) - { - return sumGradients - alpha; - } - else if (sumGradients < - alpha) - { - return sumGradients + alpha; - } - - return 0; - } - -}; -} // namespace mlpack - -#endif From 9d040070ef702f39a03d3c70792445b6a0c89773 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Thu, 27 Jun 2024 14:28:49 +0200 Subject: [PATCH 042/212] Change to const and remove the assigned values Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 6627fb0e2a..3d8f40eb46 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -82,11 +82,11 @@ namespace mlpack { template struct GetFillType { - static constexpr decltype(arma::fill::none) none = arma::fill::none; - static constexpr decltype(arma::fill::zeros) zeros = arma::fill::zeros; - static constexpr decltype(arma::fill::ones) ones = arma::fill::ones; - static constexpr decltype(arma::fill::randu) randu = arma::fill::randu; - static constexpr decltype(arma::fill::randn) randn = arma::fill::randn; + static const decltype(arma::fill::none) none; + static const decltype(arma::fill::zeros) zeros; + static const decltype(arma::fill::ones) ones; + static const decltype(arma::fill::randu) randu; + static const decltype(arma::fill::randn) randn; }; #ifdef MLPACK_HAS_COOT @@ -95,11 +95,11 @@ namespace mlpack { typename = typename std::enable_if::value>::type*> struct GetFillType { - static constexpr decltype(coot::fill::none) none = coot::fill::none; - static constexpr decltype(coot::fill::zeros) zeros = coot::fill::zeros; - static constexpr decltype(coot::fill::ones) ones = coot::fill::ones; - static constexpr decltype(coot::fill::randu) randu = coot::fill::randu; - static constexpr decltype(coot::fill::randn) randn = coot::fill::randn; + static const decltype(coot::fill::none) none; + static const decltype(coot::fill::zeros) zeros; + static const decltype(coot::fill::ones) ones; + static const decltype(coot::fill::randu) randu; + static const decltype(coot::fill::randn) randn; }; #endif @@ -109,11 +109,11 @@ namespace mlpack { template struct GetFillType { - static constexpr decltype(arma::fill::none) none = arma::fill::none; - static constexpr decltype(arma::fill::zeros) zeros = arma::fill::zeros; - static constexpr decltype(arma::fill::ones) ones = arma::fill::ones; - static constexpr decltype(arma::fill::randu) randu = arma::fill::randu; - static constexpr decltype(arma::fill::randn) randn = arma::fill::randn; + static const decltype(arma::fill::none) none; + static const decltype(arma::fill::zeros) zeros; + static const decltype(arma::fill::ones) ones; + static const decltype(arma::fill::randu) randu; + static const decltype(arma::fill::randn) randn; }; #ifdef MLPACK_HAS_COOT @@ -122,11 +122,11 @@ namespace mlpack { typename = typename std::enable_if::value>::type*> struct GetFillType { - static constexpr decltype(coot::fill::none) none = coot::fill::none; - static constexpr decltype(coot::fill::zeros) zeros = coot::fill::zeros; - static constexpr decltype(coot::fill::ones) ones = coot::fill::ones; - static constexpr decltype(coot::fill::randu) randu = coot::fill::randu; - static constexpr decltype(coot::fill::randn) randn = coot::fill::randn; + static const decltype(coot::fill::none) none; + static const decltype(coot::fill::zeros) zeros; + static const decltype(coot::fill::ones) ones; + static const decltype(coot::fill::randu) randu; + static const decltype(coot::fill::randn) randn; }; #endif From 407891b58b1fad47f0c178bf3c75987115ae2141 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Fri, 28 Jun 2024 00:00:32 +0200 Subject: [PATCH 043/212] feat: convolution optimization --- .../methods/ann/layer/convolution_impl.hpp | 90 +++++++++++-------- 1 file changed, 52 insertions(+), 38 deletions(-) diff --git a/src/mlpack/methods/ann/layer/convolution_impl.hpp b/src/mlpack/methods/ann/layer/convolution_impl.hpp index 209466701b..cf3b766e8a 100644 --- a/src/mlpack/methods/ann/layer/convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/convolution_impl.hpp @@ -357,6 +357,7 @@ void ConvolutionType< } } + template< typename ForwardConvolutionRule, typename BackwardConvolutionRule, @@ -382,38 +383,41 @@ void ConvolutionType< inMaps * higherInDimensions * batchSize); gTemp.zeros(); - const bool usingPadding = - (padWLeft != 0 || padWRight != 0 || - padHTop != 0 || padHBottom != 0); + const bool usingPadding = + (padWLeft != 0 || padWRight != 0 || padHTop != 0 || padHBottom != 0); - CubeType dilatedMappedError( - mappedError.n_rows * (strideWidth == 1 ? 1 : strideWidth - 1), - mappedError.n_cols * (strideHeight == 1 ? 1 : strideHeight - 1), - mappedError.n_slices - ); - #pragma omp parallel for collapse(3) - for (size_t i = 0; i < mappedError.n_slices; ++i) + // To perform the backward pass, we need to rotate all the filters. + CubeType rotatedFilters(weight.n_rows, + weight.n_cols, weight.n_slices); + + // To perform the backward pass, we need to dilate all the mappedError. + CubeType dilatedMappedError; + if (strideHeight == 1 && strideWidth == 1) { - for (size_t j = 0; j < mappedError.n_cols; ++j) + MakeAlias(dilatedMappedError, mappedError, mappedError.n_rows, + mappedError.n_cols, mappedError.n_slices); + } + else + { + dilatedMappedError.zeros(mappedError.n_rows * strideWidth - + (strideWidth - 1), mappedError.n_cols * strideHeight - + (strideHeight - 1), mappedError.n_slices); + #pragma omp parallel for collapse(3) schedule(static) + for (size_t i = 0; i < mappedError.n_slices; ++i) { - for (size_t k = 0; k < mappedError.n_rows; ++k) + for (size_t j = 0; j < mappedError.n_cols; ++j) { - if (strideHeight > 1 || strideWidth > 1) + for (size_t k = 0; k < mappedError.n_rows; ++k) { dilatedMappedError(k * strideWidth, j * strideHeight, i) = mappedError(k, j, i); } - else - { - dilatedMappedError(k, j, i) = mappedError(k, j, i); - } } } } - CubeType rotatedFilters(weight.n_rows, weight.n_cols, weight.n_slices); - #pragma omp parallel for - for (size_t map = 0; map < weight.n_slices; ++map) + #pragma omp parallel for schedule(static) + for (size_t map = 0; map < (size_t) (maps * inMaps); ++map) { Rotate180(weight.slice(map), rotatedFilters.slice(map)); } @@ -424,32 +428,40 @@ void ConvolutionType< MakeAlias(outputCube, output, apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); - #pragma omp parallel for collapse(2) + // See Forward() for the overall iteration strategy. + #pragma omp parallel for schedule(dynamic) for (size_t offset = 0; offset < (higherInDimensions * batchSize); ++offset) { - for (size_t inMap = 0; inMap < (size_t) inMaps; ++inMap) + const size_t fullInputOffset = offset * inMaps; + const size_t fullOutputOffset = offset * maps; + + // Iterate over input maps. + for (size_t inMap = 0; inMap < (size_t) inMaps; ++inMap) + { + // Iterate over output maps. + MatType& curG = outputCube.slice(inMap + fullInputOffset); + for (size_t outMap = 0; outMap < maps; ++outMap) { - MatType& curG = outputCube.slice(inMap + offset * inMaps); - for (size_t outMap = 0; outMap < maps; ++outMap) - { - BackwardConvolutionRule::Convolution( - dilatedMappedError.slice(outMap + offset * maps), - rotatedFilters.slice((outMap * inMaps) + inMap), - curG, - strideWidth, - strideHeight, - 1, - 1, - true); - } + BackwardConvolutionRule::Convolution( + dilatedMappedError.slice(outMap + fullOutputOffset), + rotatedFilters.slice((outMap * inMaps) + inMap), + curG, + 1, + 1, + 1, + 1, + true); } + } } + MatType temp(padding.OutputDimensions()[0] * padding.OutputDimensions()[1] * inMaps * higherInDimensions, batchSize); CubeType tempCube; MakeAlias(tempCube, temp, padding.OutputDimensions()[0], padding.OutputDimensions()[1], inMaps * higherInDimensions * batchSize); paddingBackward.Forward(output, temp); + if (usingPadding) { gTemp = tempCube.tube( @@ -463,6 +475,8 @@ void ConvolutionType< gTemp = tempCube; } } + + template< typename ForwardConvolutionRule, typename BackwardConvolutionRule, @@ -523,8 +537,8 @@ void ConvolutionType< tempCube.slice(inMap + fullInputOffset), curError, gradientTemp.slice((outMap * inMaps) + inMap), - strideWidth, - strideHeight, + 1, + 1, strideWidth, strideHeight, true); @@ -661,4 +675,4 @@ void ConvolutionType< } // namespace mlpack -#endif +#endif \ No newline at end of file From 7342aee6dc78c2eacb8ca315e9084c0b796f6754 Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Fri, 28 Jun 2024 08:39:27 +0200 Subject: [PATCH 044/212] remove line-breaks --- src/mlpack/methods/ann/layer/convolution_impl.hpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/mlpack/methods/ann/layer/convolution_impl.hpp b/src/mlpack/methods/ann/layer/convolution_impl.hpp index cf3b766e8a..db9ac2a432 100644 --- a/src/mlpack/methods/ann/layer/convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/convolution_impl.hpp @@ -357,7 +357,6 @@ void ConvolutionType< } } - template< typename ForwardConvolutionRule, typename BackwardConvolutionRule, @@ -454,14 +453,12 @@ void ConvolutionType< } } } - MatType temp(padding.OutputDimensions()[0] * padding.OutputDimensions()[1] * inMaps * higherInDimensions, batchSize); CubeType tempCube; MakeAlias(tempCube, temp, padding.OutputDimensions()[0], padding.OutputDimensions()[1], inMaps * higherInDimensions * batchSize); paddingBackward.Forward(output, temp); - if (usingPadding) { gTemp = tempCube.tube( @@ -476,7 +473,6 @@ void ConvolutionType< } } - template< typename ForwardConvolutionRule, typename BackwardConvolutionRule, @@ -675,4 +671,4 @@ void ConvolutionType< } // namespace mlpack -#endif \ No newline at end of file +#endif From 801ba91ce97f0e35a046d352d2cabcf1df5f3d69 Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Fri, 28 Jun 2024 18:28:08 +0530 Subject: [PATCH 045/212] minor fix --- .../methods/decision_tree/gain_functions/gain_functions.hpp | 1 - src/mlpack/methods/decision_tree/gain_functions/sse_gain.hpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/decision_tree/gain_functions/gain_functions.hpp b/src/mlpack/methods/decision_tree/gain_functions/gain_functions.hpp index f94e653de9..a73fcd19c6 100644 --- a/src/mlpack/methods/decision_tree/gain_functions/gain_functions.hpp +++ b/src/mlpack/methods/decision_tree/gain_functions/gain_functions.hpp @@ -1,6 +1,5 @@ #include "gini_gain.hpp" #include "information_gain.hpp" -#include "log_gain.hpp" #include "mad_gain.hpp" #include "mse_gain.hpp" #include "sse_gain.hpp" \ No newline at end of file diff --git a/src/mlpack/methods/decision_tree/gain_functions/sse_gain.hpp b/src/mlpack/methods/decision_tree/gain_functions/sse_gain.hpp index c0351fca7f..b369128aaa 100644 --- a/src/mlpack/methods/decision_tree/gain_functions/sse_gain.hpp +++ b/src/mlpack/methods/decision_tree/gain_functions/sse_gain.hpp @@ -10,8 +10,8 @@ * 3-clause BSD license along with mlpack. If not, see * http://www.opensource.org/licenses/BSD-3-Clause for more information. */ -#ifndef MLPACK_METHODS_XGBOOST_LOSS_FUNCTIONS_SSE_LOSS_HPP -#define MLPACK_METHODS_XGBOOST_LOSS_FUNCTIONS_SSE_LOSS_HPP +#ifndef MLPACK_METHODS_DECISION_TREE_SSE_GAIN_HPP +#define MLPACK_METHODS_DECISION_TREE_SSE_GAIN_HPP #include From 1d7e4bbc73482c9f47b10de40c042bd44660773f Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Fri, 28 Jun 2024 19:00:58 +0530 Subject: [PATCH 046/212] updated path --- src/mlpack/tests/xgboost_test.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/mlpack/tests/xgboost_test.cpp b/src/mlpack/tests/xgboost_test.cpp index 4b3d1d533a..57dbbc53dc 100644 --- a/src/mlpack/tests/xgboost_test.cpp +++ b/src/mlpack/tests/xgboost_test.cpp @@ -10,7 +10,7 @@ * http://www.opensource.org/licenses/BSD-3-Clause for more information. */ #include -#include +#include #include "catch.hpp" #include "serialization.hpp" @@ -18,7 +18,7 @@ using namespace mlpack; /** - * Test that the initial prediction is calculated correctly for SSE loss. + * Test that the initial prediction is calculated correctly for SSE gain. */ TEST_CASE("SSEInitialPredictionTest", "[XGBTest]") { @@ -26,12 +26,12 @@ TEST_CASE("SSEInitialPredictionTest", "[XGBTest]") double initPred = 5.5; - SSELoss Loss; + SSEGain Loss; REQUIRE(Loss.InitialPrediction(values) == initPred); } /** - * Test that output leaf value is calculated correctly for SSE Loss. + * Test that output leaf value is calculated correctly for SSE gain. */ TEST_CASE("SSELeafValueTest", "[XGBTest]") { @@ -42,14 +42,14 @@ TEST_CASE("SSELeafValueTest", "[XGBTest]") // Actual output leaf value. double leafValue = -0.075; - SSELoss Loss; + SSEGain Loss; (void) Loss.Evaluate(input, weights); REQUIRE(Loss.OutputLeafValue(input, weights) == leafValue); } /** - * Test that the gain is computed correctly for SSE Loss. + * Test that the gain is computed correctly for SSE gain. */ TEST_CASE("SSEGainTest", "[XGBTest]") { @@ -60,6 +60,6 @@ TEST_CASE("SSEGainTest", "[XGBTest]") // Actual gain value. double gain = 0.05625; - SSELoss Loss; + SSEGain Loss; REQUIRE(Loss.Evaluate(input, weights) == gain); } From cbee98dfd6c99ee3d74eaf2432d6be52b4e09b8f Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Fri, 28 Jun 2024 20:13:35 +0530 Subject: [PATCH 047/212] minor fix --- src/mlpack/methods/decision_tree/gain_functions/mse_gain.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/decision_tree/gain_functions/mse_gain.hpp b/src/mlpack/methods/decision_tree/gain_functions/mse_gain.hpp index f81ffe0781..8709b3e9ae 100644 --- a/src/mlpack/methods/decision_tree/gain_functions/mse_gain.hpp +++ b/src/mlpack/methods/decision_tree/gain_functions/mse_gain.hpp @@ -14,7 +14,7 @@ #define MLPACK_METHODS_DECISION_TREE_MSE_GAIN_HPP #include -#include "mlpack/methods/decision_tree/utils.hpp" +#include namespace mlpack { From 7b8d6323284da959467ad672f0ea52857c667cd5 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Fri, 28 Jun 2024 18:43:48 +0100 Subject: [PATCH 048/212] fixed ComputeOutputDimensions --- .../ann/layer/nearest_interpolation.hpp | 45 +---- .../ann/layer/nearest_interpolation_impl.hpp | 164 +++++++----------- 2 files changed, 67 insertions(+), 142 deletions(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp index 717b214cdb..9713055098 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp @@ -33,12 +33,9 @@ class NearestInterpolationType : public Layer public: //! Create the NearestInterpolation object. NearestInterpolationType(); + NearestInterpolationType(const double scaleFactor); + NearestInterpolationType(const std::vector scaleFactors); - NearestInterpolationType(const size_t inRowSize, - const size_t inColSize, - const size_t outRowSize, - const size_t outColSize, - const size_t depth); NearestInterpolationType* Clone() const { return new NearestInterpolationType(*this); } @@ -78,27 +75,10 @@ class NearestInterpolationType : public Layer const MatType& gradient, MatType& output); + //! Compute the output dimensions of the layer, based on the internal values + //! of `InputDimensions()`. void ComputeOutputDimensions(); - //! Get the row size of the input. - size_t const& InRowSize() const { return inRowSize; } - //! Modify the row size of the input. - size_t& InRowSize() { return inRowSize; } - //! Get the column size of the input. - size_t const& InColSize() const { return inColSize; } - //! Modify the column size of the input. - size_t& InColSize() { return inColSize; } - //! Get the row size of the output. - size_t const& OutRowSize() const { return outRowSize; } - //! Modify the row size of the output. - size_t& OutRowSize() { return outRowSize; } - //! Get the column size of the output. - size_t const& OutColSize() const { return outColSize; } - //! Modify the column size of the output. - size_t& OutColSize() { return outColSize; } - //! Get the depth of the input. - size_t const& InDepth() const { return depth; } - //! Modify the depth of the input. - size_t& InDepth() { return depth; } + /** * Serialize the layer. */ @@ -106,18 +86,9 @@ class NearestInterpolationType : public Layer void serialize(Archive& ar, const uint32_t /* version */); private: - //! Locally stored row size of the input. - size_t inRowSize; - //! Locally stored column size of the input. - size_t inColSize; - //! Locally stored row size of the output. - size_t outRowSize; - //! Locally stored column size of the input. - size_t outColSize; - //! Locally stored depth of the input. - size_t depth; - //! Locally stored number of input points. - size_t batchSize; + //! Vector of scale factors to scale different dimensions. + //! If the data has multiple dimensions, but scaleFactors has 1 value, it will scale all axes based on this value. + std::vector scaleFactors; }; // class NearestInterpolation typedef NearestInterpolationType NearestInterpolation; diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index fa898c87a9..223ed2b0cf 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -19,31 +19,26 @@ namespace mlpack { template NearestInterpolationType::NearestInterpolationType(): - Layer(), - inRowSize(0), - inColSize(0), - outRowSize(0), - outColSize(0), - depth(0), - batchSize(0) + Layer() { // Nothing to do here. } template NearestInterpolationType:: -NearestInterpolationType(const size_t inRowSize, - const size_t inColSize, - const size_t outRowSize, - const size_t outColSize, - const size_t depth) : +NearestInterpolationType(const double scaleFactor) : + Layer() +{ + scaleFactors = std::vector(2); + scaleFactors[0] = scaleFactor; + scaleFactors[1] = scaleFactor; +} + +template +NearestInterpolationType:: +NearestInterpolationType(const std::vector scaleFactors) : Layer(), - inRowSize(inRowSize), - inColSize(inColSize), - outRowSize(outRowSize), - outColSize(outColSize), - depth(depth), - batchSize(0) + scaleFactors(scaleFactors) { // Nothing to do here. } @@ -52,12 +47,7 @@ template NearestInterpolationType:: NearestInterpolationType(const NearestInterpolationType& other) : Layer(), - inRowSize(other.inRowSize), - inColSize(other.inColSize), - outRowSize(other.outRowSize), - outColSize(other.outColSize), - depth(other.depth), - batchSize(other.batchSize) + scaleFactors(other.scaleFactors) { // Nothing to do here. } @@ -66,12 +56,7 @@ template NearestInterpolationType:: NearestInterpolationType(NearestInterpolationType&& other) : Layer(std::move(other)), - inRowSize(std::move(other.inRowSize)), - inColSize(std::move(other.inColSize)), - outRowSize(std::move(other.outRowSize)), - outColSize(std::move(other.outColSize)), - depth(std::move(other.depth)), - batchSize(std::move(other.batchSize)) + scaleFactors(std::move(other.scaleFactors)) { // Nothing to do here. } @@ -84,12 +69,7 @@ operator=(const NearestInterpolationType& other) if (&other != this) { Layer::operator=(other); - inRowSize = other.inRowSize; - inColSize = other.inColSize; - outRowSize = other.outRowSize; - outColSize = other.outColSize; - depth = other.depth; - batchSize = other.batchSize; + scaleFactors = other.scaleFactors; } return *this; } @@ -102,12 +82,7 @@ operator=(NearestInterpolationType&& other) if (&other != this) { Layer::operator=(std::move(other)); - inRowSize = std::move(other.inRowSize); - inColSize = std::move(other.inColSize); - outRowSize = std::move(other.outRowSize); - outColSize = std::move(other.outColSize); - depth = std::move(other.depth); - batchSize = std::move(other.batchSize); + scaleFactors = std::move(other.scaleFactors); } return *this; } @@ -116,39 +91,33 @@ template void NearestInterpolationType::Forward( const MatType& input, MatType& output) { - batchSize = input.n_cols; - if (output.is_empty()) - output.set_size(outRowSize * outColSize * depth, batchSize); - else - { - assert(output.n_rows == outRowSize * outColSize * depth); - assert(output.n_cols == batchSize); - } + size_t channels = this->inputDimensions[0]; - assert(inRowSize >= 2); - assert(inColSize >= 2); + size_t outRowSize = this->outputDimensions[1]; + size_t outColSize = this->outputDimensions[2]; + + size_t inRowSize = this->inputDimensions[1]; + size_t inColSize = this->inputDimensions[2]; + + assert(output.n_rows == channels); + assert(output.n_cols == outRowSize * outColSize); arma::cube inputAsCube; arma::cube outputAsCube; - MakeAlias(inputAsCube, input, inRowSize, inColSize, depth * batchSize, 0, false); - MakeAlias(outputAsCube, output, outRowSize, outColSize, depth * batchSize, 0, true); + MakeAlias(inputAsCube, input, channels, inRowSize, inColSize, 0, false); + MakeAlias(outputAsCube, output, channels, outRowSize, outColSize, 0, true); - double scaleRow = (double) inRowSize / (double) outRowSize; - double scaleCol = (double) inColSize / (double) outColSize; - - for (size_t i = 0; i < outRowSize; ++i) + for (size_t i = 0; i < channels; ++i) { - const size_t rOrigin = std::floor(i * scaleRow); - - for (size_t j = 0; j < outColSize; ++j) + for (size_t j = 0; j < outRowSize; ++j) { - const size_t cOrigin = std::floor(j * scaleCol); - - for (size_t k = 0; k < depth * batchSize; ++k) + size_t rOrigin = std::floor(j * 1.0f / scaleFactors[0]); + for (size_t k = 0; k < outColSize; ++k) { - outputAsCube(i, j, k) = inputAsCube.slice(k)( - rOrigin, cOrigin); + size_t cOrigin = std::floor(k * 1.0f / scaleFactors[1]); + + outputAsCube(i, j, k) = inputAsCube(i, rOrigin, cOrigin); } } } @@ -160,47 +129,32 @@ void NearestInterpolationType::Backward( const MatType& gradient, MatType& output) { - if (output.is_empty()) - { - output.zeros(inRowSize * inColSize * depth, batchSize); - } - else - { - assert(output.n_rows == inRowSize * inColSize * depth); - assert(output.n_cols == batchSize); - } + size_t channels = this->inputDimensions[0]; - assert(outRowSize >= 2); - assert(outColSize >= 2); + size_t outRowSize = this->outputDimensions[1]; + size_t outColSize = this->outputDimensions[2]; + + size_t inRowSize = this->inputDimensions[1]; + size_t inColSize = this->inputDimensions[2]; + + assert(output.n_rows == channels); + assert(output.n_cols == inRowSize * inColSize); arma::cube outputAsCube; arma::cube gradientAsCube; - MakeAlias(outputAsCube, output, inRowSize, inColSize, depth * batchSize, 0, true); - MakeAlias(gradientAsCube, gradient, outRowSize, outColSize, depth * batchSize, 0, false); + MakeAlias(outputAsCube, output, channels, inRowSize, inColSize, 0, true); + MakeAlias(gradientAsCube, gradient, channels, outRowSize, outColSize, 0, false); - double scaleRow = (double)(inRowSize) / outRowSize; - double scaleCol = (double)(inColSize) / outColSize; - - if (gradient.n_elem == output.n_elem) + for (size_t i = 0; i < channels; ++i) { - outputAsCube = gradientAsCube; - } - else - { - for (size_t i = 0; i < outRowSize; ++i) + for (size_t j = 0; j < outRowSize; ++j) { - const size_t rOrigin = std::floor(i * scaleRow); - - for (size_t j = 0; j < outColSize; ++j) + size_t rOrigin = std::floor(j * 1.0f / scaleFactors[0]); + for (size_t k = 0; k < outColSize; ++k) { - const size_t cOrigin = std::floor(j * scaleCol); - - for (size_t k = 0; k < depth * batchSize; ++k) - { - outputAsCube(rOrigin, cOrigin, k) += - gradientAsCube(i, j, k); - } + size_t cOrigin = std::floor(k * 1.0f / scaleFactors[1]); + outputAsCube(i, rOrigin, cOrigin) += gradientAsCube(i, j, k); } } } @@ -209,8 +163,12 @@ void NearestInterpolationType::Backward( template void NearestInterpolationType::ComputeOutputDimensions() { - this->outputDimensions[0] = outRowSize * outColSize * depth; - this->outputDimensions[1] = batchSize; + assert(this->inputDimensions.size() - 1 == scaleFactors.size()); + this->outputDimensions = this->inputDimensions; + for (size_t i = 1; i < this->InputDimensions().size(); i++) + { + this->outputDimensions[i] = std::round((double)this->outputDimensions[i] * scaleFactors[i-1]); + } } template @@ -218,11 +176,7 @@ template void NearestInterpolationType::serialize( Archive& ar, const uint32_t /* version */) { - ar(CEREAL_NVP(inRowSize)); - ar(CEREAL_NVP(inColSize)); - ar(CEREAL_NVP(outRowSize)); - ar(CEREAL_NVP(outColSize)); - ar(CEREAL_NVP(depth)); + ar(CEREAL_NVP(scaleFactors)); } } // namespace mlpack From 2b132d8c9fcd51a0633996c6e7d47a17de78f34c Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Fri, 28 Jun 2024 18:44:25 +0100 Subject: [PATCH 049/212] added note to adapt layer using scale factor --- .../methods/ann/layer/not_adapted/bicubic_interpolation.hpp | 2 ++ .../methods/ann/layer/not_adapted/bilinear_interpolation.hpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/mlpack/methods/ann/layer/not_adapted/bicubic_interpolation.hpp b/src/mlpack/methods/ann/layer/not_adapted/bicubic_interpolation.hpp index e9e1144928..8daf91f89d 100644 --- a/src/mlpack/methods/ann/layer/not_adapted/bicubic_interpolation.hpp +++ b/src/mlpack/methods/ann/layer/not_adapted/bicubic_interpolation.hpp @@ -40,6 +40,8 @@ class BicubicInterpolation //! Create the Bicubic Interpolation object. BicubicInterpolation(); + // TODO: use scaleFactors instead of outRowSize and outColSize + /** * The constructor for the Bicubic Interpolation. * diff --git a/src/mlpack/methods/ann/layer/not_adapted/bilinear_interpolation.hpp b/src/mlpack/methods/ann/layer/not_adapted/bilinear_interpolation.hpp index 7f2297f771..dcb182afc0 100644 --- a/src/mlpack/methods/ann/layer/not_adapted/bilinear_interpolation.hpp +++ b/src/mlpack/methods/ann/layer/not_adapted/bilinear_interpolation.hpp @@ -42,6 +42,8 @@ class BilinearInterpolationType : public Layer //! Create the BilinearInterpolationType object. BilinearInterpolationType(); + // TODO: use scaleFactors instead of outRowSize and outColSize + /** * The constructor for the Bilinear Interpolation. The input size will be set * by the given input when the layer is used. From 76e7bbc37508a9af1ffe0c644e7591f4413cfc2e Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Fri, 28 Jun 2024 18:48:19 +0100 Subject: [PATCH 050/212] updated tests --- ...ion_test.cpp => nearest_interpolation.cpp} | 63 ++++++++++--------- src/mlpack/tests/ann/layer_test.cpp | 2 +- 2 files changed, 34 insertions(+), 31 deletions(-) rename src/mlpack/tests/ann/layer/{nearest_interpolation_test.cpp => nearest_interpolation.cpp} (54%) diff --git a/src/mlpack/tests/ann/layer/nearest_interpolation_test.cpp b/src/mlpack/tests/ann/layer/nearest_interpolation.cpp similarity index 54% rename from src/mlpack/tests/ann/layer/nearest_interpolation_test.cpp rename to src/mlpack/tests/ann/layer/nearest_interpolation.cpp index 20ab7c33e2..85aac53c64 100644 --- a/src/mlpack/tests/ann/layer/nearest_interpolation_test.cpp +++ b/src/mlpack/tests/ann/layer/nearest_interpolation.cpp @@ -22,43 +22,43 @@ using namespace mlpack; /** * Simple test for the NearestInterpolation layer */ -TEST_CASE("SimpleNearestInterpolationLayerTest", "[ANNLayerTest]") +TEST_CASE("NearestInterpolationLayerTest", "[ANNLayerTest]") { - // Tested output against torch.nn.Upsample(mode="nearest"). arma::mat input, output, unzoomedOutput, expectedOutput; - size_t inRowSize = 2; + size_t inColSize = 2; - size_t outRowSize = 5; - size_t outColSize = 7; - size_t depth = 1; - input.zeros(inRowSize * inColSize * depth, 1); + size_t inRowSize = 2; + size_t channels = 1; + + double scaleFactor = 2.0f; + + input.zeros(channels, inRowSize * inColSize); + output.zeros(channels, inRowSize * scaleFactor * inColSize * scaleFactor); + unzoomedOutput = input; input[0] = 1.0; - input[1] = 3.0; - input[2] = 2.0; + input[1] = 2.0; + input[2] = 3.0; input[3] = 4.0; - NearestInterpolation layer(inRowSize, inColSize, outRowSize, - outColSize, depth); - expectedOutput << 1.0000 << 1.0000 << 1.0000 << 1.0000 << 2.0000 - << 2.0000 << 2.0000 << arma::endr - << 1.0000 << 1.0000 << 1.0000 << 1.0000 << 2.0000 - << 2.0000 << 2.0000 << arma::endr - << 1.0000 << 1.0000 << 1.0000 << 1.0000 << 2.0000 - << 2.0000 << 2.0000 << arma::endr - << 3.0000 << 3.0000 << 3.0000 << 3.0000 << 4.0000 - << 4.0000 << 4.0000 << arma::endr - << 3.0000 << 3.0000 << 3.0000 << 3.0000 << 4.0000 - << 4.0000 << 4.0000 << arma::endr; - expectedOutput.reshape(35, 1); + mlpack::NearestInterpolation layer({scaleFactor, scaleFactor}); + layer.InputDimensions() = { channels, inRowSize, inColSize }; + layer.ComputeOutputDimensions(); + + expectedOutput << 1.0000 << 1.0000 << 2.0000 << 2.0000 + << 1.0000 << 1.0000 << 2.0000 << 2.0000 + << 3.0000 << 3.0000 << 4.0000 << 4.0000 + << 3.0000 << 3.0000 << 4.0000 << 4.0000 << arma::endr; + + expectedOutput.reshape(1, 16); layer.Forward(input, output); CheckMatrices(output - expectedOutput, arma::zeros(output.n_rows), 1e-4); expectedOutput.clear(); - expectedOutput << 12.0000 << 18.0000 << arma::endr - << 24.0000 << 24.0000 << arma::endr; - expectedOutput.reshape(4, 1); + expectedOutput << 4.0000 << 8.0000 + << 12.0000 << 16.0000 << arma::endr; + expectedOutput.reshape(1, 4); layer.Backward(output, output, unzoomedOutput); CheckMatrices(unzoomedOutput - expectedOutput, arma::zeros(input.n_rows), 1e-4); @@ -66,13 +66,16 @@ TEST_CASE("SimpleNearestInterpolationLayerTest", "[ANNLayerTest]") arma::mat input1, output1, unzoomedOutput1, expectedOutput1; inRowSize = 2; inColSize = 3; - outRowSize = 17; - outColSize = 23; + input1 << 1 << 2 << 3 << arma::endr << 4 << 5 << 6 << arma::endr; - input1.reshape(6, 1); - NearestInterpolation layer1(inRowSize, inColSize, outRowSize, - outColSize, depth); + input1.reshape(1, 6); + output1.zeros(1, 17*23); + unzoomedOutput1.zeros(1, 6); + mlpack::NearestInterpolation layer1({17/2.0f, 23/3.0f}); + + layer1.InputDimensions() = { channels, 2, 3 }; + layer1.ComputeOutputDimensions(); layer1.Forward(input1, output1); layer1.Backward(output1, output1, unzoomedOutput1); diff --git a/src/mlpack/tests/ann/layer_test.cpp b/src/mlpack/tests/ann/layer_test.cpp index ac3c1a3f4e..d074f3d715 100644 --- a/src/mlpack/tests/ann/layer_test.cpp +++ b/src/mlpack/tests/ann/layer_test.cpp @@ -39,7 +39,7 @@ #include "layer/log_softmax.cpp" #include "layer/max_pooling.cpp" #include "layer/mean_pooling.cpp" -#include "layer/nearest_interpolation_test.cpp" +#include "layer/nearest_interpolation.cpp" #include "layer/padding.cpp" #include "layer/parametric_relu.cpp" #include "layer/relu6.cpp" From 840dcf4a82d1d66f7db436fe3a3c6e744c251305 Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Mon, 1 Jul 2024 13:14:17 +0530 Subject: [PATCH 051/212] resolved some conflicts --- .../{ => split_functions}/best_binary_categorical_split.hpp | 0 .../best_binary_categorical_split_impl.hpp | 2 +- .../methods/decision_tree/split_functions/split_functions.hpp | 3 ++- 3 files changed, 3 insertions(+), 2 deletions(-) rename src/mlpack/methods/decision_tree/{ => split_functions}/best_binary_categorical_split.hpp (100%) rename src/mlpack/methods/decision_tree/{ => split_functions}/best_binary_categorical_split_impl.hpp (99%) diff --git a/src/mlpack/methods/decision_tree/best_binary_categorical_split.hpp b/src/mlpack/methods/decision_tree/split_functions/best_binary_categorical_split.hpp similarity index 100% rename from src/mlpack/methods/decision_tree/best_binary_categorical_split.hpp rename to src/mlpack/methods/decision_tree/split_functions/best_binary_categorical_split.hpp diff --git a/src/mlpack/methods/decision_tree/best_binary_categorical_split_impl.hpp b/src/mlpack/methods/decision_tree/split_functions/best_binary_categorical_split_impl.hpp similarity index 99% rename from src/mlpack/methods/decision_tree/best_binary_categorical_split_impl.hpp rename to src/mlpack/methods/decision_tree/split_functions/best_binary_categorical_split_impl.hpp index c5be2c4de2..bd0c8f62c3 100644 --- a/src/mlpack/methods/decision_tree/best_binary_categorical_split_impl.hpp +++ b/src/mlpack/methods/decision_tree/split_functions/best_binary_categorical_split_impl.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/all_categorical_split_impl.hpp + * @file methods/decision_tree/split_functions/all_categorical_split_impl.hpp * @author Nikolay Apanasov (nikolay@apanasov.org) * * Implementation of the BestBinaryCategoricalSplit categorical split class. diff --git a/src/mlpack/methods/decision_tree/split_functions/split_functions.hpp b/src/mlpack/methods/decision_tree/split_functions/split_functions.hpp index 067be112ec..63d1d5cb5a 100644 --- a/src/mlpack/methods/decision_tree/split_functions/split_functions.hpp +++ b/src/mlpack/methods/decision_tree/split_functions/split_functions.hpp @@ -1,3 +1,4 @@ #include "all_categorical_split.hpp" #include "best_binary_numeric_split.hpp" -#include "random_binary_numeric_split.hpp" \ No newline at end of file +#include "random_binary_numeric_split.hpp" +#include "best_binary_categorical_split.hpp" \ No newline at end of file From 45cc092bd15482eef000b3b1692bc7be60d46128 Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Mon, 1 Jul 2024 13:22:31 +0530 Subject: [PATCH 052/212] minor fix --- src/mlpack/methods/decision_tree/decision_tree.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/decision_tree/decision_tree.hpp b/src/mlpack/methods/decision_tree/decision_tree.hpp index 373837965e..73d557ebbb 100644 --- a/src/mlpack/methods/decision_tree/decision_tree.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree.hpp @@ -15,9 +15,9 @@ #include -#include -#include -#include +#include "gain_functions/gain_functions.hpp" +#include "split_functions/split_functions.hpp" +#include "select_functions/select_functions.hpp" namespace mlpack { From 7dfaf3e40a38bc0cca9b4540ac311038b25ce541 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Mon, 1 Jul 2024 11:21:54 +0200 Subject: [PATCH 053/212] Remove the code that is related to the arma 12 Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 72 ++++++++++------------------------ 1 file changed, 21 insertions(+), 51 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 3d8f40eb46..6d3f8184cd 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -77,59 +77,29 @@ namespace mlpack { #endif -#if (ARMA_VERSION_MAJOR >= 12) // By default, assume that we are using an Armadillo object. - template - struct GetFillType - { - static const decltype(arma::fill::none) none; - static const decltype(arma::fill::zeros) zeros; - static const decltype(arma::fill::ones) ones; - static const decltype(arma::fill::randu) randu; - static const decltype(arma::fill::randn) randn; - }; - - #ifdef MLPACK_HAS_COOT - // If the matrix type is a Bandicoot type, use Bandicoot fill objects instead. - template::value>::type*> - struct GetFillType - { - static const decltype(coot::fill::none) none; - static const decltype(coot::fill::zeros) zeros; - static const decltype(coot::fill::ones) ones; - static const decltype(coot::fill::randu) randu; - static const decltype(coot::fill::randn) randn; - }; - #endif - -#else - - // By default, assume that we are using an Armadillo object. - template - struct GetFillType - { - static const decltype(arma::fill::none) none; - static const decltype(arma::fill::zeros) zeros; - static const decltype(arma::fill::ones) ones; - static const decltype(arma::fill::randu) randu; - static const decltype(arma::fill::randn) randn; - }; - - #ifdef MLPACK_HAS_COOT - // If the matrix type is a Bandicoot type, use Bandicoot fill objects instead. - template::value>::type*> - struct GetFillType - { - static const decltype(coot::fill::none) none; - static const decltype(coot::fill::zeros) zeros; - static const decltype(coot::fill::ones) ones; - static const decltype(coot::fill::randu) randu; - static const decltype(coot::fill::randn) randn; - }; - #endif + template + struct GetFillType + { + static const decltype(arma::fill::none) none; + static const decltype(arma::fill::zeros) zeros; + static const decltype(arma::fill::ones) ones; + static const decltype(arma::fill::randu) randu; + static const decltype(arma::fill::randn) randn; + }; +#ifdef MLPACK_HAS_COOT + // If the matrix type is a Bandicoot type, use Bandicoot fill objects instead. + template::value>::type*> + struct GetFillType + { + static const decltype(coot::fill::none) none; + static const decltype(coot::fill::zeros) zeros; + static const decltype(coot::fill::ones) ones; + static const decltype(coot::fill::randu) randu; + static const decltype(coot::fill::randn) randn; + }; #endif } // namespace mlpack From 3e932b7c010f7f099b999409431194be876c81dd Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Tue, 2 Jul 2024 23:39:18 +0530 Subject: [PATCH 054/212] changed gain_function to fitness_function --- src/mlpack/methods/decision_tree/decision_tree.hpp | 2 +- src/mlpack/methods/decision_tree/decision_tree_regressor.hpp | 2 +- .../fitness_functions.hpp} | 0 .../{gain_functions => fitness_functions}/gini_gain.hpp | 0 .../{gain_functions => fitness_functions}/information_gain.hpp | 0 .../{gain_functions => fitness_functions}/mad_gain.hpp | 0 .../{gain_functions => fitness_functions}/mse_gain.hpp | 0 .../{gain_functions => fitness_functions}/sse_gain.hpp | 0 8 files changed, 2 insertions(+), 2 deletions(-) rename src/mlpack/methods/decision_tree/{gain_functions/gain_functions.hpp => fitness_functions/fitness_functions.hpp} (100%) rename src/mlpack/methods/decision_tree/{gain_functions => fitness_functions}/gini_gain.hpp (100%) rename src/mlpack/methods/decision_tree/{gain_functions => fitness_functions}/information_gain.hpp (100%) rename src/mlpack/methods/decision_tree/{gain_functions => fitness_functions}/mad_gain.hpp (100%) rename src/mlpack/methods/decision_tree/{gain_functions => fitness_functions}/mse_gain.hpp (100%) rename src/mlpack/methods/decision_tree/{gain_functions => fitness_functions}/sse_gain.hpp (100%) diff --git a/src/mlpack/methods/decision_tree/decision_tree.hpp b/src/mlpack/methods/decision_tree/decision_tree.hpp index 73d557ebbb..244cdec24e 100644 --- a/src/mlpack/methods/decision_tree/decision_tree.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree.hpp @@ -15,7 +15,7 @@ #include -#include "gain_functions/gain_functions.hpp" +#include "fitness_functions/fitness_functions.hpp" #include "split_functions/split_functions.hpp" #include "select_functions/select_functions.hpp" diff --git a/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp b/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp index be4f6d975c..41ea252475 100644 --- a/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree_regressor.hpp @@ -15,7 +15,7 @@ #include -#include "gain_functions/gain_functions.hpp" +#include "fitness_functions/fitness_functions.hpp" #include "split_functions/split_functions.hpp" #include "select_functions/select_functions.hpp" diff --git a/src/mlpack/methods/decision_tree/gain_functions/gain_functions.hpp b/src/mlpack/methods/decision_tree/fitness_functions/fitness_functions.hpp similarity index 100% rename from src/mlpack/methods/decision_tree/gain_functions/gain_functions.hpp rename to src/mlpack/methods/decision_tree/fitness_functions/fitness_functions.hpp diff --git a/src/mlpack/methods/decision_tree/gain_functions/gini_gain.hpp b/src/mlpack/methods/decision_tree/fitness_functions/gini_gain.hpp similarity index 100% rename from src/mlpack/methods/decision_tree/gain_functions/gini_gain.hpp rename to src/mlpack/methods/decision_tree/fitness_functions/gini_gain.hpp diff --git a/src/mlpack/methods/decision_tree/gain_functions/information_gain.hpp b/src/mlpack/methods/decision_tree/fitness_functions/information_gain.hpp similarity index 100% rename from src/mlpack/methods/decision_tree/gain_functions/information_gain.hpp rename to src/mlpack/methods/decision_tree/fitness_functions/information_gain.hpp diff --git a/src/mlpack/methods/decision_tree/gain_functions/mad_gain.hpp b/src/mlpack/methods/decision_tree/fitness_functions/mad_gain.hpp similarity index 100% rename from src/mlpack/methods/decision_tree/gain_functions/mad_gain.hpp rename to src/mlpack/methods/decision_tree/fitness_functions/mad_gain.hpp diff --git a/src/mlpack/methods/decision_tree/gain_functions/mse_gain.hpp b/src/mlpack/methods/decision_tree/fitness_functions/mse_gain.hpp similarity index 100% rename from src/mlpack/methods/decision_tree/gain_functions/mse_gain.hpp rename to src/mlpack/methods/decision_tree/fitness_functions/mse_gain.hpp diff --git a/src/mlpack/methods/decision_tree/gain_functions/sse_gain.hpp b/src/mlpack/methods/decision_tree/fitness_functions/sse_gain.hpp similarity index 100% rename from src/mlpack/methods/decision_tree/gain_functions/sse_gain.hpp rename to src/mlpack/methods/decision_tree/fitness_functions/sse_gain.hpp From 6b432d98763135c63ea858d4cc5034bc05191a02 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 2 Jul 2024 22:03:02 -0400 Subject: [PATCH 055/212] Handle different behavior of .array() in NumPy 2.0.0 in a backwards-compatible way. --- src/mlpack/bindings/python/mlpack/matrix_utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mlpack/bindings/python/mlpack/matrix_utils.py b/src/mlpack/bindings/python/mlpack/matrix_utils.py index dc6362cc43..cd4b1ad59e 100644 --- a/src/mlpack/bindings/python/mlpack/matrix_utils.py +++ b/src/mlpack/bindings/python/mlpack/matrix_utils.py @@ -160,7 +160,10 @@ def to_matrix_with_info(x, dtype, copy=False): dims = len(x) d = np.zeros([dims]) - out = np.array(x, dtype=dtype, copy=copy) # Try to avoid copy... + if copy: + out = np.asarray(x, dtype=dtype, copy=True) + else: + out = np.asarray(x, dtype=dtype, copy=None) # Since we don't have a great way to check if these are using the same # memory location, we will probe manually (ugh). From 8da4b3f9cecada14167787295bc4e3d536c476fd Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 2 Jul 2024 22:06:05 -0400 Subject: [PATCH 056/212] Update HISTORY.md. --- HISTORY.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 05062d0867..88bf11f34b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -4,7 +4,9 @@ _????-??-??_ - * Distribute STB headers as part of R package (#3724, #3726). + * Distribute STB headers as part of R package (#3724, #3726). + + * Update Python bindings to support NumPy 2.x (#3752). ## mlpack 4.4.0 From e1d31b6fbc4cff6f93fd5abc71d65d25b5fba471 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 2 Jul 2024 23:00:52 -0400 Subject: [PATCH 057/212] Oops, array() was intended! --- src/mlpack/bindings/python/mlpack/matrix_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mlpack/bindings/python/mlpack/matrix_utils.py b/src/mlpack/bindings/python/mlpack/matrix_utils.py index cd4b1ad59e..294896b28d 100644 --- a/src/mlpack/bindings/python/mlpack/matrix_utils.py +++ b/src/mlpack/bindings/python/mlpack/matrix_utils.py @@ -161,9 +161,9 @@ def to_matrix_with_info(x, dtype, copy=False): d = np.zeros([dims]) if copy: - out = np.asarray(x, dtype=dtype, copy=True) + out = np.array(x, dtype=dtype, copy=True) else: - out = np.asarray(x, dtype=dtype, copy=None) + out = np.array(x, dtype=dtype, copy=None) # Since we don't have a great way to check if these are using the same # memory location, we will probe manually (ugh). From 612273e861f44492b8961c03b8cf4ead2969f1ca Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 4 Jul 2024 13:33:11 -0400 Subject: [PATCH 058/212] Add an example of turning MahalanobisDistance::Q() back into a transformation matrix. --- doc/user/core.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/user/core.md b/doc/user/core.md index 0caaea7459..ffa49b9fe8 100644 --- a/doc/user/core.md +++ b/doc/user/core.md @@ -1281,6 +1281,15 @@ std::cout << "Squared Mahalanobis distance on 32-bit floating point data:" << std::endl; std::cout << " - Points 3 and 5: " << d1 << "." << std::endl; std::cout << " - Points 11 and 31: " << d2 << "." << std::endl; + +// Note that a transformation matrix can be recovered from Q with a QR +// decomposition. +arma::mat recoveredW = arma::qr(md.Q()); +if (arma::norm(recoveredW * dataset - transformedDataset, "F") < 1e-5) +{ + std::cout << "Data transformed with QR decomposition of Q matrix is " + << "the same as data transformed with W." << std::endl; +} ``` --- From 52a72d46e3edc14b678d1d91e838ebec6d99d5d5 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 4 Jul 2024 13:33:48 -0400 Subject: [PATCH 059/212] Document NCA and adapt its API to match the rest of mlpack in a backwards-compatible way. --- doc/user/methods/nca.md | 385 ++++++++++++++++++ src/mlpack/methods/nca/nca.hpp | 98 ++++- src/mlpack/methods/nca/nca_impl.hpp | 85 +++- src/mlpack/methods/nca/nca_main.cpp | 35 +- .../nca/nca_softmax_error_function.hpp | 41 +- .../nca/nca_softmax_error_function_impl.hpp | 103 +++-- src/mlpack/tests/callback_test.cpp | 5 +- src/mlpack/tests/nca_test.cpp | 152 ++++--- 8 files changed, 738 insertions(+), 166 deletions(-) create mode 100644 doc/user/methods/nca.md diff --git a/doc/user/methods/nca.md b/doc/user/methods/nca.md new file mode 100644 index 0000000000..c46c5b2584 --- /dev/null +++ b/doc/user/methods/nca.md @@ -0,0 +1,385 @@ +## NCA + +The `NCA` class implements neighborhood components analysis, which can be used +as both a linear dimensionality reduction technique and a distance learning +technique (also called metric learning). Neighborhood components analysis finds +a linear transformation of the dataset that improves `k`-nearest-neighbor +classification performance. + +Note that `NCA` is a computationally intensive technique (each optimization +iteration takes time quadratic in the data size!), and may be slow to run even +for datasets of only moderate size. + +#### Simple usage example: + +```c++ +// Learn a distance metric that improves kNN classification performance. + +// All data and labels are uniform random; 10 dimensional data, 5 classes. +// Replace with a data::Load() call or similar for a real application. +arma::mat dataset(10, 1000, arma::fill::randu); // 1000 points. +arma::Row labels = + arma::randi>(1000, arma::distr_param(0, 4)); + +mlpack::NCA nca; // Step 1: create object. +arma::mat distance; +nca.LearnDistance(dataset, labels, distance); // Step 2: learn distance. + +// `distance` can now be used as a transformation matrix for the data. +arma::mat transformedData = distance * dataset; +// Or, you can create a MahalanobisDistance to evaluate points in the +// transformed dataset space. +mlpack::MahalanobisDistance d(distance); + +std::cout << "Distance between points 0 and 1:" << std::endl; +std::cout << " - Before NCA: " + << mlpack::EuclideanDistance::Evaluate(dataset.col(0), dataset.col(1)) + << "." << std::endl; +std::cout << " - After NCA: " + << d.Evaluate(dataset.col(0), dataset.col(1)) << "." << std::endl; +``` +

More examples...

+ +#### Quick links: + + * [Constructors](#constructors): create `NCA` objects. + * [`LearnDistance()`](#learning-distances): learn distance metrics. + * [Other functionality](#other-functionality) for loading and saving. + * [Examples](#simple-examples) of simple usage and integration with other + techniques. + +#### See also: + + + + * [mlpack distance metrics](core.md#metrics) + * [`LMNN`](lmnn.md) + * [Metric learning on Wikipedia](https://en.wikipedia.org/wiki/Similarity_learning#Metric_learning) + * [Neighborhood Components Analysis on Wikipedia](https://en.wikipedia.org/wiki/Neighbourhood_components_analysis) + * [Neighbourhood Components Analysis (pdf)](https://proceedings.neurips.cc/paper_files/paper/2004/file/42fe880812925e520249e808937738d2-Paper.pdf) + +### Constructors + + * `nca = NCA()` + - Create an `NCA` object with default parameters. + +--- + + * `nca = NCA()` + * `nca = NCA(distance)` + - Create an `NCA` object using a custom [`DistanceType`](core.md#metrics). + - An instantiated `DistanceType` can optionally be passed with the `distance` + parameter. + - Using a custom `DistanceType` means that `LearnDistance()` will learn a + linear transformation for the data *in the metric space of the custom + `DistanceType`*. + * This means any learned distance may not necessarily improve + classification performance with the + [Euclidean distance](../core.md#lmetric). + * Instead, classification performance will be improved when the learned + distance is used with the given `DistanceType` only. + - Any mlpack `DistanceType` can be used as a drop-in replacement, or a + [custom `DistanceType`](../../developer/distances.md). + * A list of mlpack's provided distance metrics can be found + [here](../core.md#distances). + - ***Note: be sure that you understand the implications of a custom + `DistanceType` before using this version.*** + +--- + +### Learning Distances + +Once an `NCA` object has been created, the `LearnDistance()` method can be used +to learn a distance. + + * `nca.LearnDistance(data, labels, distance, [callbacks...])` + * `nca.LearnDistance(data, labels, distance, optimizer, [callbacks...])` + - Learn a distance metric on the given `data` and `labels`, filling + `distance` with a transformation matrix that can be used to map the data + into the space of the learned distance. + - Optionally, pass an instantiated + [ensmallen optimizer](https://www.ensmallen.org) and/or + [ensmallen callbacks](https://www.ensmallen.org/docs.html#callback-documentation) + to be used for the learning process. + - `distance` will be set to size `data.n_rows` x `data.n_rows`. + +To use `distance`, either: + + * Compute a new transformed dataset as `distance * data`, or + * Use an instantiated [`MahalanobisDistance`](../core.md#mahalanobisdistance) + with `distance` as the `Q` matrix. + +See the [examples section](#simple-examples) for more details. + +***Caveat:*** NCA operates by repeatedly computing expressions of the form +`exp(-distance.Evaluate(data.col(i), data.col(j)))` (that is, the exponential of +the negative distance between two points). When distances are very large, this +*quantity underflows to 0* and results will not be reasonable. + - This situation can be detected, usually by a result where `distance` is equal + to the identity matrix. + - Alternately, if the [`ens::ProgressBar()` + callback](https://www.ensmallen.org/docs.html#progressbar) is used, a loss of + 0 often means this situation has occurred. + - To mitigate the problem, consider scaling data such that the maximum pairwise + distance is less than 10. See the [simple examples](#simple-examples) that + use the `satellite` dataset. + +#### `LearnDistance()` Parameters: + +| **name** | **type** | **description** | +|----------|----------|-----------------| +| `data` | [`arma::mat`](../matrices.md) | [Column-major](../matrices.md#representing-data-in-mlpack) training matrix. | +| `labels` | [`arma::Row`](../matrices.md) | Training labels, [between `0` and `numClasses - 1`](../load_save.md#normalizing-labels) (inclusive). Should have length `data.n_cols`. | +| `distance` | [`arma::mat`](../matrices.md) | Output matrix to store transformation matrix representing learned distance. | + +***Note***: any matrix type can be used for `data` and `distance`, so long as +that type implements the Armadillo API. So, e.g., `arma::fmat` can be used. + +### Other Functionality + + * An `NCA` object can be serialized with + [`data::Save()` and `data::Load()`](../load_save.md#mlpack-objects). + Note that this is only meaningful if a custom `DistanceType` is being used, + and that custom `DistanceType` has state to be saved. + + * `nca.Distance()` will return the `DistanceType` being used for learning. + Unless a custom `DistanceType` was specified in the constructor, + this simply returns a [`SquaredEuclideanDistance`](../core.md#lmetric) + object. + +### Simple Examples + +Learn a distance metric to improve classification performance on the iris +dataset, and show improved performance when using +[`NaiveBayesClassifier`](nbc.md). + +```c++ +// See https://datasets.mlpack.org/iris.csv. +arma::mat dataset; +mlpack::data::Load("iris.csv", dataset, true); +// See https://datasets.mlpack.org/iris.labels.csv. +arma::Row labels; +mlpack::data::Load("iris.labels.csv", labels, true); + +// Create an NCA object and learn a distance. +arma::mat distance; +mlpack::NCA nca; +nca.LearnDistance(dataset, labels, distance); + +// The distance matrix has size equal to the dimensionality of the data. +std::cout << "Learned distance size: " << distance.n_rows << " x " + << distance.n_cols << "." << std::endl; + +// Learn a NaiveBayesClassifier model on the data and print the performance. +mlpack::NaiveBayesClassifier nbc1(dataset, labels, 3); +arma::Row predictions; +nbc1.Classify(dataset, predictions); +std::cout << "Naive Bayes Classifier without NCA: " + << arma::accu(labels == predictions) << " of " << labels.n_elem + << " correct." << std::endl; + +// Now transform the data and learn another NaiveBayesClassifier. +arma::mat transformedDataset = distance * dataset; +mlpack::NaiveBayesClassifier nbc2(transformedDataset, labels, 3); +nbc2.Classify(transformedDataset, predictions); +std::cout << "Naive Bayes Classifier with NCA: " + << arma::accu(labels == predictions) << " of " << labels.n_elem + << " correct." << std::endl; +``` + +--- + +Learn a distance metric on the satellite dataset, using 32-bit floating point to +represent the data and metric. + +```c++ +// See https://datasets.mlpack.org/ionosphere.csv. +arma::fmat dataset; +mlpack::data::Load("ionosphere.csv", dataset, true); + +// The labels are the last row of the dataset. +arma::Row labels = + arma::conv_to>::from(dataset.row(dataset.n_rows - 1)); +dataset.shed_row(dataset.n_rows - 1); + +// Create an NCA object and learn distance on float32 data. +// Pass a progress bar callback, and a configured SGD optimizer that reduces the +// number of epochs to 3 (so this example runs quickly; more would be required +// in most real-world situations!). +arma::fmat distance; +mlpack::NCA nca; + +nca.LearnDistance(dataset, labels, distance, ens::PrintLoss()); + +// We want to compute six quantities: +// +// - Average distance to points of the same class before NCA. +// - Average distance to points of the same class after NCA, using +// MahalanobisDistance. +// - Average distance to points of the same class after NCA, using the +// transformed dataset. +// +// - The same three quantities above, but for points of the other class. +// +// NCA should reduce the average distance to points in the same class, while +// increasing the average distance to points in other classes. +float distSums[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; +size_t sameCount = 0; +arma::fmat q = distance.t() * distance; +mlpack::MahalanobisDistance md(std::move(q)); +arma::fmat transformedDataset = distance * dataset; +for (size_t i = 1; i < dataset.n_cols; ++i) +{ + const double d1 = mlpack::EuclideanDistance::Evaluate( + dataset.col(0), dataset.col(i)); + const double d2 = md.Evaluate(dataset.col(0), dataset.col(i)); + const double d3 = mlpack::EuclideanDistance::Evaluate( + transformedDataset.col(0), transformedDataset.col(i)); + + // Determine whether the point has the same label as point 0. + if (labels[i] == labels[0]) + { + distSums[0] += d1; + distSums[1] += d2; + distSums[2] += d3; + ++sameCount; + } + else + { + distSums[3] += d1; + distSums[4] += d2; + distSums[5] += d3; + } +} + +// Turn the results into average distances across the class. +distSums[0] /= sameCount; +distSums[1] /= sameCount; +distSums[2] /= sameCount; +distSums[3] /= (dataset.n_cols - sameCount); +distSums[4] /= (dataset.n_cols - sameCount); +distSums[5] /= (dataset.n_cols - sameCount); + +// Print the results. +std::cout << "Average distance between point 0 and other points of the same " + << "class:" << std::endl; +std::cout << " - Before NCA: " << distSums[0] << "." + << std::endl; +std::cout << " - After NCA (with MahalanobisDistance): " << distSums[1] << "." + << std::endl; +std::cout << " - After NCA (with transformed dataset): " << distSums[2] << "." + << std::endl; +std::cout << std::endl; + +std::cout << "Average distance between point 0 and points of other classes: " + << std::endl; +std::cout << " - Before NCA: " << distSums[3] << "." + << std::endl; +std::cout << " - After NCA (with MahalanobisDistance): " << distSums[4] << "." + << std::endl; +std::cout << " - After NCA (with transformed dataset): " << distSums[5] << "." + << std::endl; +std::cout << std::endl; + +std::cout << "Ratio of other-class to same-class distances:" << std::endl; +std::cout << "(We expect this to go up.)" << std::endl; +std::cout << " - Before NCA: " << (distSums[3] / distSums[0]) << "." + << std::endl; +std::cout << " - After NCA: " << (distSums[5] / distSums[2]) << "." + << std::endl; +``` + +--- + +Learn a distance metric on the iris dataset, using the L-BFGS optimizer with +callbacks. + +```c++ +// See https://datasets.mlpack.org/iris.csv. +arma::mat dataset; +mlpack::data::Load("iris.csv", dataset, true); +// See https://datasets.mlpack.org/iris.labels.csv. +arma::Row labels; +mlpack::data::Load("iris.labels.csv", labels, true); + +// Learn a distance with ensmallen's L-BFGS optimizer. +ens::L_BFGS lbfgs; +lbfgs.NumBasis() = 5; +lbfgs.MaxIterations() = 1000; + +arma::mat distance; +mlpack::NCA nca; + +// Use callbacks that print the loss at each iteration, and then print a final +// optimization report. +nca.LearnDistance(dataset, labels, distance, lbfgs, ens::PrintLoss(), + ens::Report()); +``` + +--- + + + +Learn a distance metric on the satellite dataset, but instead of using the +Euclidean distance as the underlying metric, use the inner-product distance of +the [`PolynomialKernel`](../core.md#polynomialkernel) with the +[`IPMetric`](../core.md#ipmetric) class. The distance metric learning is +therefore performed in kernel space. + +```c++ +// See https://datasets.mlpack.org/vehicle.csv. +arma::mat dataset; +mlpack::data::Load("vehicle.csv", dataset, true); + +// The labels are contained as the last row of the dataset. +arma::Row labels = + arma::conv_to>::from(dataset.row(dataset.n_rows - 1)); +dataset.shed_row(dataset.n_rows - 1); + +// Because typical distances between points in the vehicle dataset are large, +// we will center the dataset and scale it to have points in the unit ball. +// (That is, all points will have values in each dimension between -1 and 1.) +// This means that the maximum pairwise distance is 2. +dataset.each_col() -= arma::mean(dataset, 1); +dataset /= arma::max(arma::max(arma::abs(dataset))); + +// Create the NCA object and optimize. Use Nesterov momentum SGD, printing a +// progress bar during optimization. +mlpack::NCA nca; +arma::mat distance; +nca.LearnDistance(dataset, labels, distance, ens::NesterovMomentumSGD(), + ens::ProgressBar()); + +// Now inspect distances between points with the Euclidean distance and with the +// inner product distance. +arma::mat transformedDataset = distance * dataset; + +// Points 0 and 1 have the same label (0). See their original distance---with +// both the Euclidean and Manhattan distances---and their transformed distances. +// We expect these points to get closer together, in the Manhattan distance. +const double d1 = mlpack::ManhattanDistance::Evaluate( + dataset.col(0), dataset.col(1)); +const double d2 = mlpack::ManhattanDistance::Evaluate( + transformedDataset.col(0), transformedDataset.col(1)); + +std::cout << "Distance between points 0 and 1 (same class):" << std::endl; +std::cout << " - Manhattan distance:" << std::endl; +std::cout << " * Before NCA: " << d1 << std::endl; +std::cout << " * After NCA: " << d2 << std::endl; +std::cout << std::endl; + +// Point 3 has a different label. We therefore expect this point to get further +// from point 0 with the Manhattan distance, but not necessarily with the +// Euclidean distance. +const double d3 = mlpack::ManhattanDistance::Evaluate( + dataset.col(0), dataset.col(3)); +const double d4 = mlpack::ManhattanDistance::Evaluate( + transformedDataset.col(0), transformedDataset.col(3)); + +std::cout << "Distance between points 0 and 3 (different class):" << std::endl; +std::cout << " - Manhattan distance:" << std::endl; +std::cout << " * Before NCA: " << d3 << std::endl; +std::cout << " * After NCA: " << d4 << std::endl; + +// Note that point 3 has been moved further away from point 0 than point 1. +``` diff --git a/src/mlpack/methods/nca/nca.hpp b/src/mlpack/methods/nca/nca.hpp index 4bf817124c..de6c63c88a 100644 --- a/src/mlpack/methods/nca/nca.hpp +++ b/src/mlpack/methods/nca/nca.hpp @@ -18,6 +18,23 @@ namespace mlpack { +// This utility template struct detects whether the first element in a +// parameter pack is an Armadillo type. It is entirely for the deprecated +// constructor below and can be removed when that is removed during the +// release of mlpack 5.0.0. +template +struct FirstElementIsArma +{ + static constexpr bool value = false; +}; + +template +struct FirstElementIsArma +{ + static constexpr bool value = arma::is_arma_type< + typename std::remove_reference::type>::value; +}; + /** * An implementation of Neighborhood Components Analysis, both a linear * dimensionality reduction technique and a distance learning technique. The @@ -42,7 +59,7 @@ namespace mlpack { * @endcode */ template + typename DeprecatedOptimizerType = ens::StandardSGD> class NCA { public: @@ -55,10 +72,14 @@ class NCA * @param labels Input dataset labels. * @param distance Instantiated distance metric to use. */ + [[deprecated("Will be removed in mlpack 5.0.0. Pass the dataset directly to " + "LearnDistance() instead.")]] NCA(const arma::mat& dataset, const arma::Row& labels, DistanceType distance = DistanceType()); + NCA(DistanceType distance = DistanceType()); + /** * Perform Neighborhood Components Analysis. The output distance learning * matrix is written into the passed reference. If LearnDistance() is called @@ -71,32 +92,77 @@ class NCA * @param callbacks Callback function for ensmallen optimizer `OptimizerType`. * See https://www.ensmallen.org/docs.html#callback-documentation. */ - template + template::value>::type, + typename = typename std::enable_if< + !FirstElementIsArma::value + >::type> + [[deprecated("Will be removed in mlpack 5.0.0. Use the version that takes a " + "dataset as a parameter.")]] void LearnDistance(arma::mat& outputMatrix, CallbackTypes&&... callbacks); + template::value>::type> + void LearnDistance(const MatType& dataset, + const LabelsType& labels, + MatType& outputMatrix, + CallbackTypes&&... callbacks) const; + + template, + MatType + >::value>::type> + void LearnDistance(const MatType& dataset, + const LabelsType& labels, + MatType& outputMatrix, + OptimizerType& optimizer, + CallbackTypes&&... callbacks) const; + //! Get the dataset reference. - const arma::mat& Dataset() const { return dataset; } + [[deprecated("Will be removed in mlpack 5.0.0.")]] + const arma::mat& Dataset() const { return *dataset; } //! Get the labels reference. - const arma::Row& Labels() const { return labels; } + [[deprecated("Will be removed in mlpack 5.0.0.")]] + const arma::Row& Labels() const { return *labels; } //! Get the optimizer. - const OptimizerType& Optimizer() const { return optimizer; } - OptimizerType& Optimizer() { return optimizer; } + [[deprecated("Will be removed in mlpack 5.0.0. Use the LearnDistance() " + "version that takes the optimizer as a parameter instead.")]] + const DeprecatedOptimizerType& Optimizer() const { return optimizer; } + //! Modify the optimizer. + [[deprecated("Will be removed in mlpack 5.0.0. Use the LearnDistance() " + "version that takes the optimizer as a parameter instead.")]] + DeprecatedOptimizerType& Optimizer() { return optimizer; } + + //! Get the distance. + const DistanceType Distance() const { return distance; } + //! Modify the distance. + DistanceType& Distance() { return distance; } + + template + void serialize(Archive& ar, const unsigned int /* version */); private: - //! Dataset reference. - const arma::mat& dataset; - //! Labels reference. - const arma::Row& labels; + //! Dataset pointer (will be removed in mlpack 5.0.0). + const arma::mat* dataset; + //! Labels reference (will be removed in mlpack 5.0.0). + const arma::Row* labels; + //! The optimizer to use (will be removed in mlpack 5.0.0). + DeprecatedOptimizerType optimizer; //! Distance to be used. DistanceType distance; - - //! The function to optimize. - SoftmaxErrorFunction errorFunction; - - //! The optimizer to use. - OptimizerType optimizer; }; } // namespace mlpack diff --git a/src/mlpack/methods/nca/nca_impl.hpp b/src/mlpack/methods/nca/nca_impl.hpp index c0b9dd7098..07b48f608c 100644 --- a/src/mlpack/methods/nca/nca_impl.hpp +++ b/src/mlpack/methods/nca/nca_impl.hpp @@ -18,27 +18,88 @@ namespace mlpack { // Just set the internal matrix reference. -template -NCA::NCA(const arma::mat& dataset, - const arma::Row& labels, - DistanceType distance) : - dataset(dataset), - labels(labels), - distance(distance), - errorFunction(dataset, labels, distance) +template +NCA::NCA( + const arma::mat& dataset, + const arma::Row& labels, + DistanceType distance) : + dataset(&dataset), + labels(&labels), + distance(std::move(distance)) { /* Nothing to do. */ } -template -template -void NCA::LearnDistance(arma::mat& outputMatrix, +template +NCA::NCA(DistanceType distance) : + distance(std::move(distance)) +{ /* Nothing to do. */ } + +template +template +void NCA::LearnDistance( + arma::mat& outputMatrix, CallbackTypes&&... callbacks) { + if (!dataset || !labels) + { + throw std::runtime_error("NCA::LearnDistance(): cannot call without a " + "dataset!"); + } + + LearnDistance(*dataset, *labels, outputMatrix, optimizer, + std::forward(callbacks)...); +} + +template +template +void NCA::LearnDistance( + const MatType& dataset, + const LabelsType& labels, + MatType& outputMatrix, + CallbackTypes&&... callbacks) const +{ + // This should be replaced with ens::StandardSGD when the deprecated members + // are removed for mlpack 5.0.0. + DeprecatedOptimizerType opt; + LearnDistance(dataset, labels, outputMatrix, opt, + std::forward(callbacks)...); +} + +template +template +void NCA::LearnDistance( + const MatType& dataset, + const LabelsType& labels, + MatType& outputMatrix, + OptimizerType& opt, + CallbackTypes&&... callbacks) const +{ + SoftmaxErrorFunction errorFunction( + dataset, labels, distance); + // See if we were passed an initialized matrix. if ((outputMatrix.n_rows != dataset.n_rows) || (outputMatrix.n_cols != dataset.n_rows)) outputMatrix.eye(dataset.n_rows, dataset.n_rows); - optimizer.Optimize(errorFunction, outputMatrix, callbacks...); + opt.Optimize(errorFunction, outputMatrix, + std::forward(callbacks)...); +} + +template +template +void NCA::serialize( + Archive& ar, const unsigned int /* version */) +{ + ar(CEREAL_NVP(distance)); } } // namespace mlpack diff --git a/src/mlpack/methods/nca/nca_main.cpp b/src/mlpack/methods/nca/nca_main.cpp index cae98ce87c..598efe0e12 100644 --- a/src/mlpack/methods/nca/nca_main.cpp +++ b/src/mlpack/methods/nca/nca_main.cpp @@ -240,30 +240,31 @@ void BINDING_FUNCTION(util::Params& params, util::Timers& timers) // Now create the NCA object and run the optimization. timers.Start("nca_optimization"); + NCA nca; if (optimizerType == "sgd") { - NCA > nca(data, labels); - nca.Optimizer().StepSize() = stepSize; - nca.Optimizer().MaxIterations() = maxIterations; - nca.Optimizer().Tolerance() = tolerance; - nca.Optimizer().Shuffle() = shuffle; - nca.Optimizer().BatchSize() = batchSize; + ens::StandardSGD opt; + opt.StepSize() = stepSize; + opt.MaxIterations() = maxIterations; + opt.Tolerance() = tolerance; + opt.Shuffle() = shuffle; + opt.BatchSize() = batchSize; - nca.LearnDistance(distance); + nca.LearnDistance(data, labels, distance, opt); } else if (optimizerType == "lbfgs") { - NCA, ens::L_BFGS> nca(data, labels); - nca.Optimizer().NumBasis() = numBasis; - nca.Optimizer().MaxIterations() = maxIterations; - nca.Optimizer().ArmijoConstant() = armijoConstant; - nca.Optimizer().Wolfe() = wolfe; - nca.Optimizer().MinGradientNorm() = tolerance; - nca.Optimizer().MaxLineSearchTrials() = maxLineSearchTrials; - nca.Optimizer().MinStep() = minStep; - nca.Optimizer().MaxStep() = maxStep; + ens::L_BFGS opt; + opt.NumBasis() = numBasis; + opt.MaxIterations() = maxIterations; + opt.ArmijoConstant() = armijoConstant; + opt.Wolfe() = wolfe; + opt.MinGradientNorm() = tolerance; + opt.MaxLineSearchTrials() = maxLineSearchTrials; + opt.MinStep() = minStep; + opt.MaxStep() = maxStep; - nca.LearnDistance(distance); + nca.LearnDistance(data, labels, distance, opt); } timers.Stop("nca_optimization"); diff --git a/src/mlpack/methods/nca/nca_softmax_error_function.hpp b/src/mlpack/methods/nca/nca_softmax_error_function.hpp index e4165c9c12..88237d88b3 100644 --- a/src/mlpack/methods/nca/nca_softmax_error_function.hpp +++ b/src/mlpack/methods/nca/nca_softmax_error_function.hpp @@ -40,10 +40,17 @@ namespace mlpack { * operate on one point in the dataset. This is useful for optimizers like * stochastic gradient descent (see mlpack::optimization::SGD). */ -template +template, + typename DistanceType = SquaredEuclideanDistance> class SoftmaxErrorFunction { public: + // Convenience typedef for element type of data. + typedef typename MatType::elem_type ElemType; + // Convenience typedef for column vector of data. + typedef typename GetColType::type VecType; + /** * Initialize with the given kernel; useful when the kernel has some state to * store, which is set elsewhere. If no kernel is given, an empty kernel is @@ -54,8 +61,8 @@ class SoftmaxErrorFunction * @param labels Vector of class labels for each point in the dataset. * @param metric Instantiated metric (optional). */ - SoftmaxErrorFunction(const arma::mat& dataset, - const arma::Row& labels, + SoftmaxErrorFunction(const MatType& dataset, + const LabelsType& labels, DistanceType metric = DistanceType()); /** @@ -70,7 +77,7 @@ class SoftmaxErrorFunction * * @param covariance Covariance matrix of Mahalanobis distance. */ - double Evaluate(const arma::mat& covariance); + ElemType Evaluate(const MatType& covariance); /** * Evaluate the softmax objective function for the given covariance matrix on @@ -84,9 +91,9 @@ class SoftmaxErrorFunction * @param begin Index of the initial point to use for objective function. * @param batchSize Number of points to use for objective function. */ - double Evaluate(const arma::mat& covariance, - const size_t begin, - const size_t batchSize = 1); + ElemType Evaluate(const MatType& covariance, + const size_t begin, + const size_t batchSize = 1); /** * Evaluate the gradient of the softmax function for the given covariance @@ -96,7 +103,7 @@ class SoftmaxErrorFunction * @param covariance Covariance matrix of Mahalanobis distance. * @param gradient Matrix to store the calculated gradient in. */ - void Gradient(const arma::mat& covariance, arma::mat& gradient); + void Gradient(const MatType& covariance, MatType& gradient); /** * Evaluate the gradient of the softmax function for the given covariance @@ -114,7 +121,7 @@ class SoftmaxErrorFunction * @param gradient Matrix to store the calculated gradient in. */ template - void Gradient(const arma::mat& covariance, + void Gradient(const MatType& covariance, const size_t begin, GradType& gradient, const size_t batchSize = 1); @@ -122,7 +129,7 @@ class SoftmaxErrorFunction /** * Get the initial point. */ - const arma::mat GetInitialPoint() const; + const MatType GetInitialPoint() const; /** * Get the number of functions the objective function can be decomposed into. @@ -132,23 +139,23 @@ class SoftmaxErrorFunction private: //! The dataset. This is an alias until Shuffle() is called. - arma::mat dataset; + MatType dataset; //! Labels for each point in the dataset. This is an alias until Shuffle() is //! called. - arma::Row labels; + LabelsType labels; //! The instantiated metric. DistanceType distance; //! Last coordinates. Used for the non-separable Evaluate() and Gradient(). - arma::mat lastCoordinates; + MatType lastCoordinates; //! Stretched dataset. Kept internal to avoid memory reallocations. - arma::mat stretchedDataset; + MatType stretchedDataset; //! Holds calculated p_i, for the non-separable Evaluate() and Gradient(). - arma::vec p; + VecType p; //! Holds denominators for calculation of p_ij, for the non-separable //! Evaluate() and Gradient(). - arma::vec denominators; + VecType denominators; //! False if nothing has ever been precalculated (only at construction time). bool precalculated; @@ -166,7 +173,7 @@ class SoftmaxErrorFunction * * @param coordinates Coordinates matrix to use for precalculation. */ - void Precalculate(const arma::mat& coordinates); + void Precalculate(const MatType& coordinates); }; } // namespace mlpack diff --git a/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp b/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp index 2708e6e247..f0b5ca9af7 100644 --- a/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp +++ b/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp @@ -20,10 +20,10 @@ namespace mlpack { // Initialize with the given kernel. -template -SoftmaxErrorFunction::SoftmaxErrorFunction( - const arma::mat& datasetIn, - const arma::Row& labelsIn, +template +SoftmaxErrorFunction::SoftmaxErrorFunction( + const MatType& datasetIn, + const LabelsType& labelsIn, DistanceType distance) : distance(distance), precalculated(false) @@ -33,11 +33,11 @@ SoftmaxErrorFunction::SoftmaxErrorFunction( } //! Shuffle the dataset. -template -void SoftmaxErrorFunction::Shuffle() +template +void SoftmaxErrorFunction::Shuffle() { - arma::mat newDataset; - arma::Row newLabels; + MatType newDataset; + LabelsType newLabels; ShuffleData(dataset, labels, newDataset, newLabels); @@ -49,31 +49,39 @@ void SoftmaxErrorFunction::Shuffle() } //! The non-separable implementation, which uses Precalculate() to save time. -template -double SoftmaxErrorFunction::Evaluate(const arma::mat& coordinates) +template +typename MatType::elem_type +SoftmaxErrorFunction::Evaluate( + const MatType& coordinates) { // Calculate the denominators and numerators, if necessary. + std::cout << "call Evaluate()\n"; Precalculate(coordinates); + std::cout << "result: " << -accu(p) << "\n"; return -accu(p); // Sum of p_i for all i. We negate because our solver // minimizes, not maximizes. }; //! The separated objective function, which does not use Precalculate(), //! for a given batch size and from an initial index. -template -double SoftmaxErrorFunction::Evaluate(const arma::mat& coordinates, - const size_t begin, - const size_t batchSize) +template +typename MatType::elem_type +SoftmaxErrorFunction::Evaluate( + const MatType& coordinates, + const size_t begin, + const size_t batchSize) { // Unfortunately each evaluation will take O(N) time because it requires a // scan over all points in the dataset. Our objective is to compute p_i. - double denominator = 0; - double numerator = 0; - double result = 0; + ElemType denominator = 0; + ElemType numerator = 0; + ElemType result = 0; // It's quicker to do this now than one point at a time later. stretchedDataset = coordinates * dataset; + + #pragma omp parallel for reduction(+:result) for (size_t i = begin; i < begin + batchSize; ++i) { for (size_t k = 0; k < dataset.n_cols; ++k) @@ -83,7 +91,7 @@ double SoftmaxErrorFunction::Evaluate(const arma::mat& coordinates continue; // We want to evaluate exp(-D(A x_i, A x_k)). - double eval = std::exp(-distance.Evaluate( + ElemType eval = std::exp(-distance.Evaluate( stretchedDataset.unsafe_col(i), stretchedDataset.unsafe_col(k))); // If they are in the same class, update the numerator. @@ -108,11 +116,12 @@ double SoftmaxErrorFunction::Evaluate(const arma::mat& coordinates } //! The non-separable implementation, where Precalculate() is used. -template -void SoftmaxErrorFunction::Gradient(const arma::mat& coordinates, - arma::mat& gradient) +template +void SoftmaxErrorFunction::Gradient( + const MatType& coordinates, MatType& gradient) { // Calculate the denominators and numerators, if necessary. + std::cout << "call Gradient()\n"; Precalculate(coordinates); // Now, we handle the summation over i: @@ -127,22 +136,22 @@ void SoftmaxErrorFunction::Gradient(const arma::mat& coordinates, // (((p_i - (1 / p_i)) p_ik) + ((p_k - (1 / p_k)) p_ki)) x_ik x_ik^T // otherwise, add // (p_i p_ik + p_k p_ki) x_ik x_ik^T - arma::mat sum; + MatType sum; sum.zeros(stretchedDataset.n_rows, stretchedDataset.n_rows); for (size_t i = 0; i < stretchedDataset.n_cols; ++i) { for (size_t k = (i + 1); k < stretchedDataset.n_cols; ++k) { // Calculate p_ik and p_ki first. - double eval = std::exp(-distance.Evaluate( + ElemType eval = std::exp(-distance.Evaluate( stretchedDataset.unsafe_col(i), stretchedDataset.unsafe_col(k))); - double p_ik = 0, p_ki = 0; + ElemType p_ik = 0, p_ki = 0; p_ik = eval / denominators(i); p_ki = eval / denominators(k); // Subtract x_i from x_k. We are not using stretched points here. - arma::vec x_ik = dataset.col(i) - dataset.col(k); - arma::mat secondTerm = (x_ik * trans(x_ik)); + VecType x_ik = dataset.col(i) - dataset.col(k); + MatType secondTerm = (x_ik * trans(x_ik)); if (labels[i] == labels[k]) sum += ((p[i] - 1) * p_ik + (p[k] - 1) * p_ki) * secondTerm; @@ -156,19 +165,20 @@ void SoftmaxErrorFunction::Gradient(const arma::mat& coordinates, } //! The separable implementation for a given batch size and an initial index. -template +template template -void SoftmaxErrorFunction::Gradient(const arma::mat& coordinates, - const size_t begin, - GradType& gradient, - const size_t batchSize) +void SoftmaxErrorFunction::Gradient( + const MatType& coordinates, + const size_t begin, + GradType& gradient, + const size_t batchSize) { // The gradient involves two matrix terms which are eventually combined into // one. GradType firstTerm, secondTerm; // We will need to calculate p_i before this evaluation is done, so // these two variables will hold the information necessary for that. - double numerator, denominator; + ElemType numerator, denominator; gradient.zeros(coordinates.n_rows, coordinates.n_rows); @@ -189,7 +199,7 @@ void SoftmaxErrorFunction::Gradient(const arma::mat& coordinates, continue; // Calculate the numerator of p_ik. - double eval = std::exp(-distance.Evaluate( + ElemType eval = std::exp(-distance.Evaluate( stretchedDataset.unsafe_col(i), stretchedDataset.unsafe_col(k))); // If the points are in the same class, we must add to the second term of @@ -210,7 +220,7 @@ void SoftmaxErrorFunction::Gradient(const arma::mat& coordinates, } // Calculate p_i. - double p = 0; + ElemType p = 0; if (denominator == 0) { Log::Warn << "Denominator of p_" << i << " is 0!" << std::endl; @@ -231,15 +241,16 @@ void SoftmaxErrorFunction::Gradient(const arma::mat& coordinates, } } -template -const arma::mat SoftmaxErrorFunction::GetInitialPoint() const +template +const MatType +SoftmaxErrorFunction::GetInitialPoint() const { - return arma::eye(dataset.n_rows, dataset.n_rows); + return arma::eye(dataset.n_rows, dataset.n_rows); } -template -void SoftmaxErrorFunction::Precalculate( - const arma::mat& coordinates) +template +void SoftmaxErrorFunction::Precalculate( + const MatType& coordinates) { // Ensure it is the right size. if (lastCoordinates.n_rows != coordinates.n_rows || @@ -263,33 +274,41 @@ void SoftmaxErrorFunction::Precalculate( // We will do this by keeping track of the denominators for each i as well as // the numerators (the sum for all j in class of i). This will be on the // order of O((n * (n + 1)) / 2), which really isn't all that great. + std::cout << "precalculating after stretch...\n"; p.zeros(stretchedDataset.n_cols); denominators.zeros(stretchedDataset.n_cols); + #pragma omp parallel for collapse(2) for (size_t i = 0; i < stretchedDataset.n_cols; ++i) { for (size_t j = (i + 1); j < stretchedDataset.n_cols; ++j) { // Evaluate exp(-d(x_i, x_j)). - double eval = std::exp(-distance.Evaluate( + ElemType eval = std::exp(-distance.Evaluate( stretchedDataset.unsafe_col(i), stretchedDataset.unsafe_col(j))); // Add this to the denominators of both p_i and p_j: K(i, j) = K(j, i). + #pragma omp atomic denominators[i] += eval; + #pragma omp atomic denominators[j] += eval; // If i and j are the same class, add to numerator of both. if (labels[i] == labels[j]) { + #pragma omp atomic p[i] += eval; + #pragma omp atomic p[j] += eval; } } } + std::cout << "now cleanup\n"; // Divide p_i by their denominators. p /= denominators; // Clean up any bad values. + #pragma omp parallel for for (size_t i = 0; i < stretchedDataset.n_cols; ++i) { if (denominators[i] == 0.0) @@ -297,7 +316,7 @@ void SoftmaxErrorFunction::Precalculate( Log::Debug << "Denominator of p_{" << i << ", j} is 0." << std::endl; // Set to usable values. - denominators[i] = std::numeric_limits::infinity(); + denominators[i] = std::numeric_limits::infinity(); p[i] = 0; } } diff --git a/src/mlpack/tests/callback_test.cpp b/src/mlpack/tests/callback_test.cpp index bd98dcad85..942a56ddc4 100644 --- a/src/mlpack/tests/callback_test.cpp +++ b/src/mlpack/tests/callback_test.cpp @@ -170,12 +170,11 @@ TEST_CASE("NCAWithOptimizerCallback", "[CallbackTest]") " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; arma::Row labels = " 0 0 0 1 1 1 "; - NCA nca(data, labels); - arma::mat outputMatrix; std::stringstream stream; - nca.LearnDistance(outputMatrix, ens::ProgressBar(70, stream)); + NCA nca; + nca.LearnDistance(data, labels, outputMatrix, ens::ProgressBar(70, stream)); REQUIRE(stream.str().length() > 0); } diff --git a/src/mlpack/tests/nca_test.cpp b/src/mlpack/tests/nca_test.cpp index 2dcfc0909d..f4fd6a081e 100644 --- a/src/mlpack/tests/nca_test.cpp +++ b/src/mlpack/tests/nca_test.cpp @@ -26,18 +26,21 @@ using namespace ens; * The Softmax error function should return the identity matrix as its initial * point. */ -TEST_CASE("SoftmaxInitialPoint", "[NCATesT]") +TEMPLATE_TEST_CASE("SoftmaxInitialPoint", "[NCATest]", float, double) { + typedef TestType eT; + // Cheap fake dataset. - arma::mat data; + arma::Mat data; data.randu(5, 5); arma::Row labels; labels.zeros(5); - SoftmaxErrorFunction sef(data, labels); + SoftmaxErrorFunction, arma::Row, + SquaredEuclideanDistance> sef(data, labels); // Verify the initial point is the identity matrix. - arma::mat initialPoint = sef.GetInitialPoint(); + arma::Mat initialPoint = sef.GetInitialPoint(); for (int row = 0; row < 5; row++) { for (int col = 0; col < 5; col++) @@ -54,16 +57,19 @@ TEST_CASE("SoftmaxInitialPoint", "[NCATesT]") * On a simple fake dataset, ensure that the initial function evaluation is * correct. */ -TEST_CASE("SoftmaxInitialEvaluation", "[NCATesT]") +TEMPLATE_TEST_CASE("SoftmaxInitialEvaluation", "[NCATest]", float, double) { + typedef TestType eT; + // Useful but simple dataset with six points and two classes. - arma::mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + arma::Mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; arma::Row labels = " 0 0 0 1 1 1 "; - SoftmaxErrorFunction sef(data, labels); + SoftmaxErrorFunction, arma::Row, + SquaredEuclideanDistance> sef(data, labels); - double objective = sef.Evaluate(arma::eye(2, 2)); + eT objective = sef.Evaluate(arma::eye>(2, 2)); // Result painstakingly calculated by hand by rcurtin (recorded forever in his // notebook). As a result of lack of precision of the by-hand result, the @@ -75,22 +81,28 @@ TEST_CASE("SoftmaxInitialEvaluation", "[NCATesT]") * On a simple fake dataset, ensure that the initial gradient evaluation is * correct. */ -TEST_CASE("SoftmaxInitialGradient", "[NCATesT]") +TEMPLATE_TEST_CASE("SoftmaxInitialGradient", "[NCATest]", float, double) { + typedef TestType eT; + // Useful but simple dataset with six points and two classes. - arma::mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + arma::Mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; arma::Row labels = " 0 0 0 1 1 1 "; - SoftmaxErrorFunction sef(data, labels); + SoftmaxErrorFunction, arma::Row, + SquaredEuclideanDistance> sef(data, labels); - arma::mat gradient; - arma::mat coordinates = arma::eye(2, 2); + arma::Mat gradient; + arma::Mat coordinates(2, 2, arma::fill::eye); sef.Gradient(coordinates, gradient); // Results painstakingly calculated by hand by rcurtin (recorded forever in // his notebook). As a result of lack of precision of the by-hand result, the // tolerance is fairly high. + // + // UPDATE 2024: that notebook definitely got thrown away over a decade ago. I + // don't even remember what it looked like. REQUIRE(gradient(0, 0) == Approx(-0.089766).epsilon(0.0005)); REQUIRE(gradient(1, 0) == Approx(0.0).margin(1e-5)); REQUIRE(gradient(0, 1) == Approx(0.0).margin(1e-5)); @@ -101,16 +113,19 @@ TEST_CASE("SoftmaxInitialGradient", "[NCATesT]") * On optimally separated datasets, ensure that the objective function is * optimal (equal to the negative number of points). */ -TEST_CASE("SoftmaxOptimalEvaluation", "[NCATesT]") +TEMPLATE_TEST_CASE("SoftmaxOptimalEvaluation", "[NCATest]", float, double) { + typedef TestType eT; + // Simple optimal dataset. - arma::mat data = " 500 500 -500 -500;" + arma::Mat data = " 500 500 -500 -500;" " 1 0 1 0 "; arma::Row labels = " 0 0 1 1 "; - SoftmaxErrorFunction sef(data, labels); + SoftmaxErrorFunction, arma::Row, + SquaredEuclideanDistance> sef(data, labels); - double objective = sef.Evaluate(arma::eye(2, 2)); + eT objective = sef.Evaluate(arma::eye>(2, 2)); // Use a very close tolerance for optimality; we need to be sure this function // gives optimal results correctly. @@ -120,17 +135,20 @@ TEST_CASE("SoftmaxOptimalEvaluation", "[NCATesT]") /** * On optimally separated datasets, ensure that the gradient is zero. */ -TEST_CASE("SoftmaxOptimalGradient", "[NCATesT]") +TEMPLATE_TEST_CASE("SoftmaxOptimalGradient", "[NCATest]", float, double) { + typedef TestType eT; + // Simple optimal dataset. - arma::mat data = " 500 500 -500 -500;" + arma::Mat data = " 500 500 -500 -500;" " 1 0 1 0 "; arma::Row labels = " 0 0 1 1 "; - SoftmaxErrorFunction sef(data, labels); + SoftmaxErrorFunction, arma::Row, + SquaredEuclideanDistance> sef(data, labels); - arma::mat gradient; - sef.Gradient(arma::eye(2, 2), gradient); + arma::Mat gradient; + sef.Gradient(arma::eye>(2, 2), gradient); REQUIRE(gradient(0, 0) == Approx(0.0).margin(1e-5)); REQUIRE(gradient(0, 1) == Approx(0.0).margin(1e-5)); @@ -141,19 +159,22 @@ TEST_CASE("SoftmaxOptimalGradient", "[NCATesT]") /** * Ensure the separable objective function is right. */ -TEST_CASE("SoftmaxSeparableObjective", "[NCATesT]") +TEMPLATE_TEST_CASE("SoftmaxSeparableObjective", "[NCATest]", float, double) { + typedef TestType eT; + // Useful but simple dataset with six points and two classes. - arma::mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + arma::Mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; arma::Row labels = " 0 0 0 1 1 1 "; - SoftmaxErrorFunction sef(data, labels); + SoftmaxErrorFunction, arma::Row, + SquaredEuclideanDistance> sef(data, labels); // Results painstakingly calculated by hand by rcurtin (recorded forever in // his notebook). As a result of lack of precision of the by-hand result, the // tolerance is fairly high. - arma::mat coordinates = arma::eye(2, 2); + arma::Mat coordinates = arma::eye>(2, 2); REQUIRE(sef.Evaluate(coordinates, 0, 1) == Approx(-0.22480).epsilon(0.0001)); REQUIRE(sef.Evaluate(coordinates, 1, 1) == Approx(-0.30613).epsilon(0.0001)); REQUIRE(sef.Evaluate(coordinates, 2, 1) == Approx(-0.22480).epsilon(0.0001)); @@ -165,16 +186,19 @@ TEST_CASE("SoftmaxSeparableObjective", "[NCATesT]") /** * Ensure the optimal separable objective function is right. */ -TEST_CASE("OptimalSoftmaxSeparableObjective", "[NCATesT]") +TEMPLATE_TEST_CASE("OptimalSoftmaxSeparableObjective", "[NCATest]", float, double) { + typedef TestType eT; + // Simple optimal dataset. - arma::mat data = " 500 500 -500 -500;" + arma::Mat data = " 500 500 -500 -500;" " 1 0 1 0 "; arma::Row labels = " 0 0 1 1 "; - SoftmaxErrorFunction sef(data, labels); + SoftmaxErrorFunction, arma::Row, + SquaredEuclideanDistance> sef(data, labels); - arma::mat coordinates = arma::eye(2, 2); + arma::Mat coordinates = arma::eye>(2, 2); // Use a very close tolerance for optimality; we need to be sure this function // gives optimal results correctly. @@ -187,17 +211,20 @@ TEST_CASE("OptimalSoftmaxSeparableObjective", "[NCATesT]") /** * Ensure the separable gradient is right. */ -TEST_CASE("SoftmaxSeparableGradient", "[NCATesT]") +TEMPLATE_TEST_CASE("SoftmaxSeparableGradient", "[NCATest]", float, double) { + typedef TestType eT; + // Useful but simple dataset with six points and two classes. - arma::mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + arma::Mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; arma::Row labels = " 0 0 0 1 1 1 "; - SoftmaxErrorFunction sef(data, labels); + SoftmaxErrorFunction, arma::Row, + SquaredEuclideanDistance> sef(data, labels); - arma::mat coordinates = arma::eye(2, 2); - arma::mat gradient(2, 2); + arma::Mat coordinates = arma::eye>(2, 2); + arma::Mat gradient(2, 2); sef.Gradient(coordinates, 0, gradient, 1); @@ -250,29 +277,33 @@ TEST_CASE("SoftmaxSeparableGradient", "[NCATesT]") * On our simple dataset, ensure that the NCA algorithm fully separates the * points. */ -TEST_CASE("NCASGDSimpleDataset", "[NCATesT]") +TEMPLATE_TEST_CASE("NCASGDSimpleDataset", "[NCATest]", float, double) { + typedef TestType eT; + // Useful but simple dataset with six points and two classes. - arma::mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + arma::Mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; arma::Row labels = " 0 0 0 1 1 1 "; // Huge learning rate because this is so simple. - NCA nca(data, labels); - nca.Optimizer().StepSize() = 1.2; - nca.Optimizer().MaxIterations() = 300000; - nca.Optimizer().Tolerance() = 0; - nca.Optimizer().Shuffle() = true; + ens::StandardSGD opt; + opt.StepSize() = 1.2; + opt.MaxIterations() = 300000; + opt.Tolerance() = 0; + opt.Shuffle() = true; - arma::mat outputMatrix; - nca.LearnDistance(outputMatrix); + arma::Mat outputMatrix; + NCA nca; + nca.LearnDistance(data, labels, outputMatrix, opt); // Ensure that the objective function is better now. - SoftmaxErrorFunction sef(data, labels); + SoftmaxErrorFunction, arma::Row, + SquaredEuclideanDistance> sef(data, labels); - double initObj = sef.Evaluate(arma::eye(2, 2)); - double finalObj = sef.Evaluate(outputMatrix); - arma::mat finalGradient; + eT initObj = sef.Evaluate(arma::eye>(2, 2)); + eT finalObj = sef.Evaluate(outputMatrix); + arma::Mat finalGradient; sef.Gradient(outputMatrix, finalGradient); // finalObj must be less than initObj. @@ -284,26 +315,29 @@ TEST_CASE("NCASGDSimpleDataset", "[NCATesT]") REQUIRE(arma::norm(finalGradient, 2) < 1e-4); } -TEST_CASE("NCALBFGSSimpleDataset", "[NCATesT]") +TEMPLATE_TEST_CASE("NCALBFGSSimpleDataset", "[NCATest]", float, double) { + typedef TestType eT; + // Useful but simple dataset with six points and two classes. - arma::mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + arma::Mat data = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; arma::Row labels = " 0 0 0 1 1 1 "; - // Huge learning rate because this is so simple. - NCA nca(data, labels); - nca.Optimizer().NumBasis() = 5; + L_BFGS lbfgs; + lbfgs.NumBasis() = 5; - arma::mat outputMatrix; - nca.LearnDistance(outputMatrix); + arma::Mat outputMatrix; + NCA nca; + nca.LearnDistance(data, labels, outputMatrix, lbfgs); // Ensure that the objective function is better now. - SoftmaxErrorFunction sef(data, labels); + SoftmaxErrorFunction, arma::Row, + SquaredEuclideanDistance> sef(data, labels); - double initObj = sef.Evaluate(arma::eye(2, 2)); - double finalObj = sef.Evaluate(outputMatrix); - arma::mat finalGradient; + eT initObj = sef.Evaluate(arma::eye>(2, 2)); + eT finalObj = sef.Evaluate(outputMatrix); + arma::Mat finalGradient; sef.Gradient(outputMatrix, finalGradient); // finalObj must be less than initObj. From e9ace524bd2b534d400be9d48203af5d17d4772d Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 4 Jul 2024 13:58:19 -0400 Subject: [PATCH 060/212] Clarify example (and fix it). --- doc/user/core.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/doc/user/core.md b/doc/user/core.md index ffa49b9fe8..0654103a66 100644 --- a/doc/user/core.md +++ b/doc/user/core.md @@ -1282,14 +1282,10 @@ std::cout << "Squared Mahalanobis distance on 32-bit floating point data:" std::cout << " - Points 3 and 5: " << d1 << "." << std::endl; std::cout << " - Points 11 and 31: " << d2 << "." << std::endl; -// Note that a transformation matrix can be recovered from Q with a QR -// decomposition. -arma::mat recoveredW = arma::qr(md.Q()); -if (arma::norm(recoveredW * dataset - transformedDataset, "F") < 1e-5) -{ - std::cout << "Data transformed with QR decomposition of Q matrix is " - << "the same as data transformed with W." << std::endl; -} +// Note that an equivalent transformation matrix can be recovered from Q with +// an upper Cholesky decomposition (Q -> R.t() * R). +arma::mat recoveredW = arma::chol(md.Q(), "lower"); +// A transformed dataset can be created with `(recoveredW * dataset)`. ``` --- From a23414db5b8584c7d2acc6360c6b2b80690f4e10 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Thu, 4 Jul 2024 23:45:36 +0100 Subject: [PATCH 061/212] updated coments and added const --- .../ann/layer/nearest_interpolation.hpp | 18 ++++++-- .../ann/layer/nearest_interpolation_impl.hpp | 41 +++++++++---------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp index 9713055098..d132348f25 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp @@ -1,6 +1,7 @@ +// /** - * @file methods/ann/layer/nearest_interpolation.hpp - * @author Abhinav Anand + * @filer methods/ann/layer/nearest_interpolation.hpp + * @author Andrew Furey * * mlpack is free software; you may redistribute it and/or modify it under the * terms of the 3-clause BSD license. You should have received a copy of the @@ -33,7 +34,19 @@ class NearestInterpolationType : public Layer public: //! Create the NearestInterpolation object. NearestInterpolationType(); + + /**Create NearestInterpolation Object with the same scaleFactor along + * each dimension + * + * @tparam scaleFactor Number to scale each dimension by. + */ NearestInterpolationType(const double scaleFactor); + + /**Create NearestInterpolation Object with the same scaleFactor along + * each dimension + * + * @tparam scaleFactor Numbers to scale each dimension by. + */ NearestInterpolationType(const std::vector scaleFactors); NearestInterpolationType* Clone() const { @@ -87,7 +100,6 @@ class NearestInterpolationType : public Layer private: //! Vector of scale factors to scale different dimensions. - //! If the data has multiple dimensions, but scaleFactors has 1 value, it will scale all axes based on this value. std::vector scaleFactors; }; // class NearestInterpolation diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index 223ed2b0cf..27b56f80e6 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -1,6 +1,6 @@ /** * @file methods/ann/layer/nearest_interpolation_impl.hpp - * @author Abhinav Anand + * @author Andrew Furey * * Implementation of the NearestInterpolation layer. * @@ -29,16 +29,17 @@ NearestInterpolationType:: NearestInterpolationType(const double scaleFactor) : Layer() { - scaleFactors = std::vector(2); - scaleFactors[0] = scaleFactor; - scaleFactors[1] = scaleFactor; + scaleFactors = std::vector(this->inputDimensions.size()); + for (size_t i = 0; i < scaleFactors.size(); i++) { + scaleFactors[i] = scaleFactor; + } } template NearestInterpolationType:: NearestInterpolationType(const std::vector scaleFactors) : Layer(), - scaleFactors(scaleFactors) + scaleFactors(std::move(scaleFactors)) { // Nothing to do here. } @@ -91,16 +92,13 @@ template void NearestInterpolationType::Forward( const MatType& input, MatType& output) { - size_t channels = this->inputDimensions[0]; + const size_t channels = this->inputDimensions[0]; - size_t outRowSize = this->outputDimensions[1]; - size_t outColSize = this->outputDimensions[2]; + const size_t outRowSize = this->outputDimensions[1]; + const size_t outColSize = this->outputDimensions[2]; - size_t inRowSize = this->inputDimensions[1]; - size_t inColSize = this->inputDimensions[2]; - - assert(output.n_rows == channels); - assert(output.n_cols == outRowSize * outColSize); + const size_t inRowSize = this->inputDimensions[1]; + const size_t inColSize = this->inputDimensions[2]; arma::cube inputAsCube; arma::cube outputAsCube; @@ -129,16 +127,13 @@ void NearestInterpolationType::Backward( const MatType& gradient, MatType& output) { - size_t channels = this->inputDimensions[0]; + const size_t channels = this->inputDimensions[0]; - size_t outRowSize = this->outputDimensions[1]; - size_t outColSize = this->outputDimensions[2]; + const size_t outRowSize = this->outputDimensions[1]; + const size_t outColSize = this->outputDimensions[2]; - size_t inRowSize = this->inputDimensions[1]; - size_t inColSize = this->inputDimensions[2]; - - assert(output.n_rows == channels); - assert(output.n_cols == inRowSize * inColSize); + const size_t inRowSize = this->inputDimensions[1]; + const size_t inColSize = this->inputDimensions[2]; arma::cube outputAsCube; arma::cube gradientAsCube; @@ -163,7 +158,9 @@ void NearestInterpolationType::Backward( template void NearestInterpolationType::ComputeOutputDimensions() { - assert(this->inputDimensions.size() - 1 == scaleFactors.size()); + if (this->inputDimensions.size() - 1 == scaleFactors.size()) { + throw std::runtime_error("Scale factors must match."); + } this->outputDimensions = this->inputDimensions; for (size_t i = 1; i < this->InputDimensions().size(); i++) { From ce930567132c358dde237eaf65244bb4ac423f49 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Fri, 5 Jul 2024 08:24:31 +0100 Subject: [PATCH 062/212] fixed row column channel order --- .../ann/layer/nearest_interpolation.hpp | 9 +-- .../ann/layer/nearest_interpolation_impl.hpp | 72 ++++++++----------- .../tests/ann/layer/nearest_interpolation.cpp | 24 ++++--- 3 files changed, 44 insertions(+), 61 deletions(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp index d132348f25..684e8510f1 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp @@ -38,14 +38,7 @@ class NearestInterpolationType : public Layer /**Create NearestInterpolation Object with the same scaleFactor along * each dimension * - * @tparam scaleFactor Number to scale each dimension by. - */ - NearestInterpolationType(const double scaleFactor); - - /**Create NearestInterpolation Object with the same scaleFactor along - * each dimension - * - * @tparam scaleFactor Numbers to scale each dimension by. + * @param scaleFactor Scale factors to scale each dimension by. */ NearestInterpolationType(const std::vector scaleFactors); diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index 27b56f80e6..ae08aea962 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -24,17 +24,6 @@ NearestInterpolationType::NearestInterpolationType(): // Nothing to do here. } -template -NearestInterpolationType:: -NearestInterpolationType(const double scaleFactor) : - Layer() -{ - scaleFactors = std::vector(this->inputDimensions.size()); - for (size_t i = 0; i < scaleFactors.size(); i++) { - scaleFactors[i] = scaleFactor; - } -} - template NearestInterpolationType:: NearestInterpolationType(const std::vector scaleFactors) : @@ -92,30 +81,29 @@ template void NearestInterpolationType::Forward( const MatType& input, MatType& output) { - const size_t channels = this->inputDimensions[0]; + const size_t channels = this->inputDimensions[2]; - const size_t outRowSize = this->outputDimensions[1]; - const size_t outColSize = this->outputDimensions[2]; + const size_t outRowSize = this->outputDimensions[0]; + const size_t outColSize = this->outputDimensions[1]; - const size_t inRowSize = this->inputDimensions[1]; - const size_t inColSize = this->inputDimensions[2]; + const size_t inRowSize = this->inputDimensions[0]; + const size_t inColSize = this->inputDimensions[1]; arma::cube inputAsCube; arma::cube outputAsCube; - MakeAlias(inputAsCube, input, channels, inRowSize, inColSize, 0, false); - MakeAlias(outputAsCube, output, channels, outRowSize, outColSize, 0, true); + MakeAlias(inputAsCube, input, inRowSize, inColSize, channels, 0, false); + MakeAlias(outputAsCube, output, outRowSize, outColSize, channels, 0, true); - for (size_t i = 0; i < channels; ++i) + for (size_t i = 0; i < outRowSize; ++i) { - for (size_t j = 0; j < outRowSize; ++j) + size_t rOrigin = std::floor(i * 1.0f / scaleFactors[0]); + for (size_t j = 0; j < outColSize; ++j) { - size_t rOrigin = std::floor(j * 1.0f / scaleFactors[0]); - for (size_t k = 0; k < outColSize; ++k) + size_t cOrigin = std::floor(j * 1.0f / scaleFactors[1]); + for (size_t k = 0; k < channels; ++k) { - size_t cOrigin = std::floor(k * 1.0f / scaleFactors[1]); - - outputAsCube(i, j, k) = inputAsCube(i, rOrigin, cOrigin); + outputAsCube(i, j, k) = inputAsCube(rOrigin, cOrigin, k); } } } @@ -127,29 +115,29 @@ void NearestInterpolationType::Backward( const MatType& gradient, MatType& output) { - const size_t channels = this->inputDimensions[0]; + const size_t channels = this->inputDimensions[2]; - const size_t outRowSize = this->outputDimensions[1]; - const size_t outColSize = this->outputDimensions[2]; + const size_t outRowSize = this->outputDimensions[0]; + const size_t outColSize = this->outputDimensions[1]; - const size_t inRowSize = this->inputDimensions[1]; - const size_t inColSize = this->inputDimensions[2]; + const size_t inRowSize = this->inputDimensions[0]; + const size_t inColSize = this->inputDimensions[1]; arma::cube outputAsCube; arma::cube gradientAsCube; - MakeAlias(outputAsCube, output, channels, inRowSize, inColSize, 0, true); - MakeAlias(gradientAsCube, gradient, channels, outRowSize, outColSize, 0, false); + MakeAlias(outputAsCube, output, inRowSize, inColSize, channels, 0, true); + MakeAlias(gradientAsCube, gradient, outRowSize, outColSize, channels, 0, false); - for (size_t i = 0; i < channels; ++i) + for (size_t i = 0; i < outRowSize; ++i) { - for (size_t j = 0; j < outRowSize; ++j) + size_t rOrigin = std::floor(i * 1.0f / scaleFactors[0]); + for (size_t j = 0; j < outColSize; ++j) { - size_t rOrigin = std::floor(j * 1.0f / scaleFactors[0]); - for (size_t k = 0; k < outColSize; ++k) + size_t cOrigin = std::floor(j * 1.0f / scaleFactors[1]); + for (size_t k = 0; k < channels; ++k) { - size_t cOrigin = std::floor(k * 1.0f / scaleFactors[1]); - outputAsCube(i, rOrigin, cOrigin) += gradientAsCube(i, j, k); + outputAsCube(rOrigin, cOrigin, k) += gradientAsCube(i, j, k); } } } @@ -158,13 +146,13 @@ void NearestInterpolationType::Backward( template void NearestInterpolationType::ComputeOutputDimensions() { - if (this->inputDimensions.size() - 1 == scaleFactors.size()) { - throw std::runtime_error("Scale factors must match."); + if (this->inputDimensions.size() - 1 != scaleFactors.size()) { + throw std::runtime_error("Scale factors must match number of rows and columns."); } this->outputDimensions = this->inputDimensions; - for (size_t i = 1; i < this->InputDimensions().size(); i++) + for (size_t i = 0; i < this->InputDimensions().size()-1; i++) { - this->outputDimensions[i] = std::round((double)this->outputDimensions[i] * scaleFactors[i-1]); + this->outputDimensions[i] = std::round((double)this->outputDimensions[i] * scaleFactors[i]); } } diff --git a/src/mlpack/tests/ann/layer/nearest_interpolation.cpp b/src/mlpack/tests/ann/layer/nearest_interpolation.cpp index 85aac53c64..5bc082546f 100644 --- a/src/mlpack/tests/ann/layer/nearest_interpolation.cpp +++ b/src/mlpack/tests/ann/layer/nearest_interpolation.cpp @@ -32,17 +32,20 @@ TEST_CASE("NearestInterpolationLayerTest", "[ANNLayerTest]") double scaleFactor = 2.0f; - input.zeros(channels, inRowSize * inColSize); - output.zeros(channels, inRowSize * scaleFactor * inColSize * scaleFactor); + input.zeros(inRowSize * inColSize, channels); + output.zeros(inRowSize * scaleFactor * inColSize * scaleFactor, channels); unzoomedOutput = input; input[0] = 1.0; input[1] = 2.0; input[2] = 3.0; input[3] = 4.0; - mlpack::NearestInterpolation layer({scaleFactor, scaleFactor}); + mlpack::NearestInterpolation layer; - layer.InputDimensions() = { channels, inRowSize, inColSize }; + + layer = mlpack::NearestInterpolation({scaleFactor, scaleFactor}); + + layer.InputDimensions() = { inRowSize, inColSize, channels }; layer.ComputeOutputDimensions(); expectedOutput << 1.0000 << 1.0000 << 2.0000 << 2.0000 @@ -50,7 +53,7 @@ TEST_CASE("NearestInterpolationLayerTest", "[ANNLayerTest]") << 3.0000 << 3.0000 << 4.0000 << 4.0000 << 3.0000 << 3.0000 << 4.0000 << 4.0000 << arma::endr; - expectedOutput.reshape(1, 16); + expectedOutput.reshape(16, 1); layer.Forward(input, output); CheckMatrices(output - expectedOutput, arma::zeros(output.n_rows), 1e-4); @@ -58,7 +61,7 @@ TEST_CASE("NearestInterpolationLayerTest", "[ANNLayerTest]") expectedOutput.clear(); expectedOutput << 4.0000 << 8.0000 << 12.0000 << 16.0000 << arma::endr; - expectedOutput.reshape(1, 4); + expectedOutput.reshape(4, 1); layer.Backward(output, output, unzoomedOutput); CheckMatrices(unzoomedOutput - expectedOutput, arma::zeros(input.n_rows), 1e-4); @@ -69,17 +72,16 @@ TEST_CASE("NearestInterpolationLayerTest", "[ANNLayerTest]") input1 << 1 << 2 << 3 << arma::endr << 4 << 5 << 6 << arma::endr; - input1.reshape(1, 6); - output1.zeros(1, 17*23); - unzoomedOutput1.zeros(1, 6); + input1.reshape(6, 1); + output1.zeros(17*23, 1); + unzoomedOutput1.zeros(6, 1); mlpack::NearestInterpolation layer1({17/2.0f, 23/3.0f}); - layer1.InputDimensions() = { channels, 2, 3 }; + layer1.InputDimensions() = { 2, 3, channels }; layer1.ComputeOutputDimensions(); layer1.Forward(input1, output1); layer1.Backward(output1, output1, unzoomedOutput1); - REQUIRE(accu(output1) - 1317.00 == Approx(0.0).margin(1e-05)); REQUIRE(accu(unzoomedOutput1) - 1317.00 == Approx(0.0).margin(1e-05)); From b699fa685b501f18f9d8fb3a18ad6e526aa024df Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Fri, 5 Jul 2024 08:43:30 +0100 Subject: [PATCH 063/212] must be 2 dimensions --- .../ann/layer/nearest_interpolation_impl.hpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index ae08aea962..14d2e30ff5 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -14,6 +14,7 @@ // In case it hasn't yet been included. #include "nearest_interpolation.hpp" +#include namespace mlpack { @@ -27,10 +28,12 @@ NearestInterpolationType::NearestInterpolationType(): template NearestInterpolationType:: NearestInterpolationType(const std::vector scaleFactors) : - Layer(), - scaleFactors(std::move(scaleFactors)) + Layer() { - // Nothing to do here. + if (scaleFactors.size() != 2) { + throw std::runtime_error("Scale factors must have 2 dimensions"); + } + this->scaleFactors = std::move(scaleFactors); } template @@ -97,10 +100,10 @@ void NearestInterpolationType::Forward( for (size_t i = 0; i < outRowSize; ++i) { - size_t rOrigin = std::floor(i * 1.0f / scaleFactors[0]); + size_t rOrigin = std::floor(i / scaleFactors[0]); for (size_t j = 0; j < outColSize; ++j) { - size_t cOrigin = std::floor(j * 1.0f / scaleFactors[1]); + size_t cOrigin = std::floor(j / scaleFactors[1]); for (size_t k = 0; k < channels; ++k) { outputAsCube(i, j, k) = inputAsCube(rOrigin, cOrigin, k); @@ -131,10 +134,10 @@ void NearestInterpolationType::Backward( for (size_t i = 0; i < outRowSize; ++i) { - size_t rOrigin = std::floor(i * 1.0f / scaleFactors[0]); + size_t rOrigin = std::floor(i / scaleFactors[0]); for (size_t j = 0; j < outColSize; ++j) { - size_t cOrigin = std::floor(j * 1.0f / scaleFactors[1]); + size_t cOrigin = std::floor(j / scaleFactors[1]); for (size_t k = 0; k < channels; ++k) { outputAsCube(rOrigin, cOrigin, k) += gradientAsCube(i, j, k); From 5b4f2fb81587ef5bbae87870f077b4ae3da70b18 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 10 Apr 2024 14:28:55 +0200 Subject: [PATCH 064/212] feat: updated Dropout Forward with find and fill --- src/mlpack/methods/ann/layer/dropout_impl.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mlpack/methods/ann/layer/dropout_impl.hpp b/src/mlpack/methods/ann/layer/dropout_impl.hpp index 1a725d149d..dca049cb4f 100644 --- a/src/mlpack/methods/ann/layer/dropout_impl.hpp +++ b/src/mlpack/methods/ann/layer/dropout_impl.hpp @@ -87,7 +87,9 @@ void DropoutType::Forward(const MatType& input, MatType& output) // Scale with input / (1 - ratio) and set values to zero with probability // 'ratio'. mask.randu(input.n_rows, input.n_cols); - mask.transform([&](double val) { return (val > ratio); }); + arma::uvec indices = arma::find(mask > ratio); + mask.zeros(); + mask.elem(indices).fill(1); output = input % mask * scale; } } From adf63a9a84f0a85464b665407ce14648383e630f Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Sun, 14 Apr 2024 18:28:29 +0200 Subject: [PATCH 065/212] struct: implemented both versions of the algorithm --- src/mlpack/methods/ann/layer/dropout_impl.hpp | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/ann/layer/dropout_impl.hpp b/src/mlpack/methods/ann/layer/dropout_impl.hpp index dca049cb4f..0955879b2f 100644 --- a/src/mlpack/methods/ann/layer/dropout_impl.hpp +++ b/src/mlpack/methods/ann/layer/dropout_impl.hpp @@ -77,15 +77,41 @@ DropoutType::operator=(DropoutType&& other) template void DropoutType::Forward(const MatType& input, MatType& output) { - // The dropout mask will not be multiplied in testing mode. + ForwardImpl(input, output); +} + +template +void DropoutType::ForwardImpl(const MatType& input, + MatType& output, + const typename std::enable_if_t< + arma::is_arma_type::value>*) +{ + if (!this->training) + { + output = input; + } + else + { + mask.randu(input.n_rows, input.n_cols); + mask.transform([&](double val) { return (val > ratio); }); + output = input % mask * scale; + } +} + +#ifdef MLPACK_HAS_COOT + +template +void DropoutType::ForwardImpl(const MatType& input, + MatType& output, + const typename std::enable_if_t< + coot::is_coot_type::value>*) +{ if (!this->training) { output = input; } else { - // Scale with input / (1 - ratio) and set values to zero with probability - // 'ratio'. mask.randu(input.n_rows, input.n_cols); arma::uvec indices = arma::find(mask > ratio); mask.zeros(); @@ -94,6 +120,8 @@ void DropoutType::Forward(const MatType& input, MatType& output) } } +#endif + template void DropoutType::Backward( const MatType& /* input */, From 4747b7c8de9f046dc5796a2980ebc72c6b56eb3a Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Sun, 14 Apr 2024 19:20:24 +0200 Subject: [PATCH 066/212] fixed structure --- src/mlpack/methods/ann/layer/dropout.hpp | 14 ++++++++++++++ src/mlpack/methods/ann/layer/dropout_impl.hpp | 12 ++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/mlpack/methods/ann/layer/dropout.hpp b/src/mlpack/methods/ann/layer/dropout.hpp index ad18e8e452..651668c5b8 100644 --- a/src/mlpack/methods/ann/layer/dropout.hpp +++ b/src/mlpack/methods/ann/layer/dropout.hpp @@ -76,6 +76,20 @@ class DropoutType : public Layer * @param output Resulting output activation. */ void Forward(const MatType& input, MatType& output); + + + /** + * Implementation of the forward pass of the dropout layer. + * + * @param input Input data used for evaluating the specified function. + * @param output Resulting output activation. + */ + void ForwardImpl(const MatType& input, MatType& output); + + #ifdef MLPACK_HAS_COOT + void ForwardImpl(const MatType& input, MatType& output); + #endif + /** * Ordinary feed backward pass of the dropout layer. diff --git a/src/mlpack/methods/ann/layer/dropout_impl.hpp b/src/mlpack/methods/ann/layer/dropout_impl.hpp index 0955879b2f..6fa01a9bf0 100644 --- a/src/mlpack/methods/ann/layer/dropout_impl.hpp +++ b/src/mlpack/methods/ann/layer/dropout_impl.hpp @@ -74,17 +74,17 @@ DropoutType::operator=(DropoutType&& other) return *this; } + template void DropoutType::Forward(const MatType& input, MatType& output) { + // The dropout mask will not be multiplied in testing mode. ForwardImpl(input, output); } template void DropoutType::ForwardImpl(const MatType& input, - MatType& output, - const typename std::enable_if_t< - arma::is_arma_type::value>*) + MatType& output) { if (!this->training) { @@ -92,6 +92,8 @@ void DropoutType::ForwardImpl(const MatType& input, } else { + // Scale with input / (1 - ratio) and set values to zero with probability + // 'ratio'. mask.randu(input.n_rows, input.n_cols); mask.transform([&](double val) { return (val > ratio); }); output = input % mask * scale; @@ -102,9 +104,7 @@ void DropoutType::ForwardImpl(const MatType& input, template void DropoutType::ForwardImpl(const MatType& input, - MatType& output, - const typename std::enable_if_t< - coot::is_coot_type::value>*) + MatType& output) { if (!this->training) { From 10d44eba4a9c20bf00e2e5099931f6f3e79694ef Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Tue, 23 Apr 2024 01:38:54 +0200 Subject: [PATCH 067/212] refactor dropout layer implementation --- src/mlpack/methods/ann/layer/dropout.hpp | 11 +++++------ src/mlpack/methods/ann/layer/dropout_impl.hpp | 13 ++++--------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/mlpack/methods/ann/layer/dropout.hpp b/src/mlpack/methods/ann/layer/dropout.hpp index 651668c5b8..6c51744245 100644 --- a/src/mlpack/methods/ann/layer/dropout.hpp +++ b/src/mlpack/methods/ann/layer/dropout.hpp @@ -84,12 +84,11 @@ class DropoutType : public Layer * @param input Input data used for evaluating the specified function. * @param output Resulting output activation. */ - void ForwardImpl(const MatType& input, MatType& output); - - #ifdef MLPACK_HAS_COOT - void ForwardImpl(const MatType& input, MatType& output); - #endif - + template + std::enable_if_t::value, void> ForwardImpl(const T& input, T& output); + + template + std::enable_if_t::value, void> ForwardImpl(const T& input, T& output); /** * Ordinary feed backward pass of the dropout layer. diff --git a/src/mlpack/methods/ann/layer/dropout_impl.hpp b/src/mlpack/methods/ann/layer/dropout_impl.hpp index 6fa01a9bf0..46ffd0ff47 100644 --- a/src/mlpack/methods/ann/layer/dropout_impl.hpp +++ b/src/mlpack/methods/ann/layer/dropout_impl.hpp @@ -74,7 +74,6 @@ DropoutType::operator=(DropoutType&& other) return *this; } - template void DropoutType::Forward(const MatType& input, MatType& output) { @@ -83,8 +82,8 @@ void DropoutType::Forward(const MatType& input, MatType& output) } template -void DropoutType::ForwardImpl(const MatType& input, - MatType& output) +std::enable_if_t::value, void> +DropoutType::ForwardImpl(const MatType& input, MatType& output) { if (!this->training) { @@ -100,11 +99,9 @@ void DropoutType::ForwardImpl(const MatType& input, } } -#ifdef MLPACK_HAS_COOT - template -void DropoutType::ForwardImpl(const MatType& input, - MatType& output) +std::enable_if_t::value, void> +DropoutType::ForwardImpl(const MatType& input, MatType& output) { if (!this->training) { @@ -120,8 +117,6 @@ void DropoutType::ForwardImpl(const MatType& input, } } -#endif - template void DropoutType::Backward( const MatType& /* input */, From 2608c9df6d07097b569a78663f11d91fb23188cd Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Tue, 23 Apr 2024 01:41:29 +0200 Subject: [PATCH 068/212] fix --- src/mlpack/methods/ann/layer/dropout.hpp | 17 ++++++++++++----- src/mlpack/methods/ann/layer/dropout_impl.hpp | 13 ++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/mlpack/methods/ann/layer/dropout.hpp b/src/mlpack/methods/ann/layer/dropout.hpp index 6c51744245..3f193ea876 100644 --- a/src/mlpack/methods/ann/layer/dropout.hpp +++ b/src/mlpack/methods/ann/layer/dropout.hpp @@ -76,7 +76,6 @@ class DropoutType : public Layer * @param output Resulting output activation. */ void Forward(const MatType& input, MatType& output); - /** * Implementation of the forward pass of the dropout layer. @@ -84,11 +83,19 @@ class DropoutType : public Layer * @param input Input data used for evaluating the specified function. * @param output Resulting output activation. */ - template - std::enable_if_t::value, void> ForwardImpl(const T& input, T& output); + template::value, int> = 0> + void ForwardImpl(const T& input, T& output); + + /** + * General implementation of the forward pass of the dropout layer. + * + * @param input Input data used for evaluating the specified function. + * @param output Resulting output activation. + */ + template::value, int> = 0> + void ForwardImpl(const T& input, T& output); + - template - std::enable_if_t::value, void> ForwardImpl(const T& input, T& output); /** * Ordinary feed backward pass of the dropout layer. diff --git a/src/mlpack/methods/ann/layer/dropout_impl.hpp b/src/mlpack/methods/ann/layer/dropout_impl.hpp index 46ffd0ff47..5b4d3f63af 100644 --- a/src/mlpack/methods/ann/layer/dropout_impl.hpp +++ b/src/mlpack/methods/ann/layer/dropout_impl.hpp @@ -74,6 +74,7 @@ DropoutType::operator=(DropoutType&& other) return *this; } + template void DropoutType::Forward(const MatType& input, MatType& output) { @@ -82,8 +83,8 @@ void DropoutType::Forward(const MatType& input, MatType& output) } template -std::enable_if_t::value, void> -DropoutType::ForwardImpl(const MatType& input, MatType& output) +template::value, int>> +void DropoutType::ForwardImpl(const T& input, T& output) { if (!this->training) { @@ -100,8 +101,8 @@ DropoutType::ForwardImpl(const MatType& input, MatType& output) } template -std::enable_if_t::value, void> -DropoutType::ForwardImpl(const MatType& input, MatType& output) +template::value, int>> +void DropoutType::ForwardImpl(const T& input, T& output) { if (!this->training) { @@ -110,9 +111,7 @@ DropoutType::ForwardImpl(const MatType& input, MatType& output) else { mask.randu(input.n_rows, input.n_cols); - arma::uvec indices = arma::find(mask > ratio); - mask.zeros(); - mask.elem(indices).fill(1); + mask = (mask > ratio); output = input % mask * scale; } } From 3db482e3af775b31ab482acb85159ce0cb212b7f Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Tue, 23 Apr 2024 01:42:03 +0200 Subject: [PATCH 069/212] remove line break --- src/mlpack/methods/ann/layer/dropout.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mlpack/methods/ann/layer/dropout.hpp b/src/mlpack/methods/ann/layer/dropout.hpp index 3f193ea876..b1b4e3909f 100644 --- a/src/mlpack/methods/ann/layer/dropout.hpp +++ b/src/mlpack/methods/ann/layer/dropout.hpp @@ -95,8 +95,6 @@ class DropoutType : public Layer template::value, int> = 0> void ForwardImpl(const T& input, T& output); - - /** * Ordinary feed backward pass of the dropout layer. * From c5f69489936f354abed6813b6479c51eef0dd334 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Fri, 21 Jun 2024 16:00:44 +0200 Subject: [PATCH 070/212] speedup using openMP and structure fixes --- src/mlpack/methods/ann/layer/dropout.hpp | 8 +++---- src/mlpack/methods/ann/layer/dropout_impl.hpp | 22 +++++++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/mlpack/methods/ann/layer/dropout.hpp b/src/mlpack/methods/ann/layer/dropout.hpp index b1b4e3909f..5780082f1c 100644 --- a/src/mlpack/methods/ann/layer/dropout.hpp +++ b/src/mlpack/methods/ann/layer/dropout.hpp @@ -83,8 +83,8 @@ class DropoutType : public Layer * @param input Input data used for evaluating the specified function. * @param output Resulting output activation. */ - template::value, int> = 0> - void ForwardImpl(const T& input, T& output); + template::value>* = 0> + void ForwardImpl(const MatType& input, MatType& output); /** * General implementation of the forward pass of the dropout layer. @@ -92,8 +92,8 @@ class DropoutType : public Layer * @param input Input data used for evaluating the specified function. * @param output Resulting output activation. */ - template::value, int> = 0> - void ForwardImpl(const T& input, T& output); + template::value>* = 0> + void ForwardImpl(const MatType& input, MatType& output); /** * Ordinary feed backward pass of the dropout layer. diff --git a/src/mlpack/methods/ann/layer/dropout_impl.hpp b/src/mlpack/methods/ann/layer/dropout_impl.hpp index 5b4d3f63af..2db6425b01 100644 --- a/src/mlpack/methods/ann/layer/dropout_impl.hpp +++ b/src/mlpack/methods/ann/layer/dropout_impl.hpp @@ -74,7 +74,6 @@ DropoutType::operator=(DropoutType&& other) return *this; } - template void DropoutType::Forward(const MatType& input, MatType& output) { @@ -82,6 +81,7 @@ void DropoutType::Forward(const MatType& input, MatType& output) ForwardImpl(input, output); } + template template::value, int>> void DropoutType::ForwardImpl(const T& input, T& output) @@ -92,11 +92,14 @@ void DropoutType::ForwardImpl(const T& input, T& output) } else { - // Scale with input / (1 - ratio) and set values to zero with probability - // 'ratio'. mask.randu(input.n_rows, input.n_cols); - mask.transform([&](double val) { return (val > ratio); }); - output = input % mask * scale; + #pragma omp parallel for collapse(2) + for (size_t i = 0; i < input.n_rows; ++i) { + for (size_t j = 0; j < input.n_cols; ++j) { + mask(i, j) = (mask(i, j) > this->ratio) ? 1.0 : 0.0; + } + } + output = input % mask * this->scale; } } @@ -111,8 +114,13 @@ void DropoutType::ForwardImpl(const T& input, T& output) else { mask.randu(input.n_rows, input.n_cols); - mask = (mask > ratio); - output = input % mask * scale; + #pragma omp parallel for collapse(2) + for (size_t i = 0; i < input.n_rows; ++i) { + for (size_t j = 0; j < input.n_cols; ++j) { + mask(i, j) = (mask(i, j) > this->ratio) ? 1.0 : 0.0; + } + } + output = input % mask * this->scale; } } From 356036de0e78b6f0c58f248b682e50b09bf97b2a Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 26 Jun 2024 19:16:59 +0200 Subject: [PATCH 071/212] resolved conflicts --- CMake/allexec2man.sh | 0 CMake/exec2man.sh | 0 scripts/build-docs.sh | 0 scripts/check-markdown-docs.sh | 0 scripts/release-mlpack.sh | 0 scripts/test-docs.sh | 0 scripts/update-website-after-release.sh | 0 src/mlpack/bindings/R/mlpack/cleanup | 0 8 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 CMake/allexec2man.sh mode change 100755 => 100644 CMake/exec2man.sh mode change 100755 => 100644 scripts/build-docs.sh mode change 100755 => 100644 scripts/check-markdown-docs.sh mode change 100755 => 100644 scripts/release-mlpack.sh mode change 100755 => 100644 scripts/test-docs.sh mode change 100755 => 100644 scripts/update-website-after-release.sh mode change 100755 => 100644 src/mlpack/bindings/R/mlpack/cleanup diff --git a/CMake/allexec2man.sh b/CMake/allexec2man.sh old mode 100755 new mode 100644 diff --git a/CMake/exec2man.sh b/CMake/exec2man.sh old mode 100755 new mode 100644 diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh old mode 100755 new mode 100644 diff --git a/scripts/check-markdown-docs.sh b/scripts/check-markdown-docs.sh old mode 100755 new mode 100644 diff --git a/scripts/release-mlpack.sh b/scripts/release-mlpack.sh old mode 100755 new mode 100644 diff --git a/scripts/test-docs.sh b/scripts/test-docs.sh old mode 100755 new mode 100644 diff --git a/scripts/update-website-after-release.sh b/scripts/update-website-after-release.sh old mode 100755 new mode 100644 diff --git a/src/mlpack/bindings/R/mlpack/cleanup b/src/mlpack/bindings/R/mlpack/cleanup old mode 100755 new mode 100644 From d215a961dd79b62675b362ede9b0a9d0217c3baa Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 26 Jun 2024 19:24:12 +0200 Subject: [PATCH 072/212] updated to mlpack style --- src/mlpack/methods/ann/layer/dropout_impl.hpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/ann/layer/dropout_impl.hpp b/src/mlpack/methods/ann/layer/dropout_impl.hpp index 2db6425b01..f93ac422e9 100644 --- a/src/mlpack/methods/ann/layer/dropout_impl.hpp +++ b/src/mlpack/methods/ann/layer/dropout_impl.hpp @@ -94,8 +94,10 @@ void DropoutType::ForwardImpl(const T& input, T& output) { mask.randu(input.n_rows, input.n_cols); #pragma omp parallel for collapse(2) - for (size_t i = 0; i < input.n_rows; ++i) { - for (size_t j = 0; j < input.n_cols; ++j) { + for (size_t i = 0; i < input.n_rows; ++i) + { + for (size_t j = 0; j < input.n_cols; ++j) + { mask(i, j) = (mask(i, j) > this->ratio) ? 1.0 : 0.0; } } @@ -115,8 +117,10 @@ void DropoutType::ForwardImpl(const T& input, T& output) { mask.randu(input.n_rows, input.n_cols); #pragma omp parallel for collapse(2) - for (size_t i = 0; i < input.n_rows; ++i) { - for (size_t j = 0; j < input.n_cols; ++j) { + for (size_t i = 0; i < input.n_rows; ++i) + { + for (size_t j = 0; j < input.n_cols; ++j) + { mask(i, j) = (mask(i, j) > this->ratio) ? 1.0 : 0.0; } } From f517f67b7efe2194db2794e7c5e1c41c5d2213cc Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 26 Jun 2024 19:27:42 +0200 Subject: [PATCH 073/212] removed unwanted files --- CMake/allexec2man.sh | 26 -- CMake/exec2man.sh | 104 ------ scripts/build-docs.sh | 432 ------------------------ scripts/check-markdown-docs.sh | 53 --- scripts/release-mlpack.sh | 218 ------------ scripts/test-docs.sh | 407 ---------------------- scripts/update-website-after-release.sh | 65 ---- 7 files changed, 1305 deletions(-) delete mode 100644 CMake/allexec2man.sh delete mode 100644 CMake/exec2man.sh delete mode 100644 scripts/build-docs.sh delete mode 100644 scripts/check-markdown-docs.sh delete mode 100644 scripts/release-mlpack.sh delete mode 100644 scripts/test-docs.sh delete mode 100644 scripts/update-website-after-release.sh diff --git a/CMake/allexec2man.sh b/CMake/allexec2man.sh deleted file mode 100644 index 885ffe73da..0000000000 --- a/CMake/allexec2man.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -set -e - -if [ $# != 2 ]; then - echo "Convert all of the executables in this directory that are not tests to man" - echo "pages in the given directory." - echo - echo "Usage:" - echo " allexec2man.sh /full/path/of/exec2man.sh output_directory/" - echo - echo "For the executable 'cheese', the file 'cheese.1.gz' will be created in the" - echo "output directory." - exit 1 -fi - -exec2man="$1" -outdir="$2" - -mkdir -p "$outdir" -for program in $(find . -type f -perm -u+x -iname 'mlpack_*' | \ - grep -v '[.]$' | \ - grep -v '_test$'); do - echo "Generating man page for $program..."; - "$exec2man" "$program" "$outdir/$program.1" -done diff --git a/CMake/exec2man.sh b/CMake/exec2man.sh deleted file mode 100644 index 3ca65b9bbc..0000000000 --- a/CMake/exec2man.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/sh -# -# Convert the output of an mlpack executable into a man page. This assumes that -# the IO subsystem is used to output help, that the executable is properly -# documented, and that the program is run in the directory that the executable -# is in. Usually, this is used by CMake on Linux/UNIX systems to generate the -# man pages. -# -# Usage: -# exec2man.sh executable_name output_file_name -# -# No warranties... -# -# @author Ryan Curtin - -set -e - -if [ $# != 2 ]; then - echo "Generates man page from the help text of an mlpack utility program." - echo "Usage: $0 mlpack_executable generated-man-page.1" - exit 1 -fi - -exec="$1" -name="$(basename "$exec")" -output="$2" - -if [ "$name" = "$exec" ]; then - # if no directory prefix with explict ./ to avoid path search - exec="./$exec" -fi - -if [ ! -x "$exec" ]; then - echo "error: cannot find executable file $exec" - exit 1 -fi - -# Get the version. -version=$("$exec" --version | sed 's/^.* \([^ ]*\)\.$/\1/') - -# Generate the synopsis. -# First, required options. -reqoptions="$("$exec" --help | \ - awk '/Required input options:/,/Optional input options:/' | \ - grep '^ --' | \ - sed 's/^ --/--/' | \ - sed 's/^--[A-Za-z0-9_-]* (\(-[A-Za-z0-9]\))/\1/' | \ - sed 's/\(^-[A-Za-z0-9]\) [^\[].*/\1/' | \ - sed 's/\(^-[A-Za-z0-9] \[[A-Za-z0-9]*\]\) .*/\1/' | \ - sed 's/\(^--[A-Za-z0-9_-]*\) [^[].*/\1/' | \ - sed 's/\(^--[A-Za-z0-9_-]* \[[A-Za-z0-9]*\]\) [^[].*/\1/' | \ - tr '\n' ' ' | \ - sed 's/\[//g' | \ - sed 's/\]//g')" - -# Then, regular options. -options="$("$exec" -h | \ - awk '/Optional input options:/,/For further information,/' | \ - grep '^ --' | \ - sed 's/^ --/--/' | \ - grep -v -- '--help' | \ - grep -v -- '--info' | \ - grep -v -- '--verbose' | \ - sed 's/^--[A-Za-z0-9_-]* (\(-[A-Za-z0-9]\))/\1/' | \ - sed 's/\(^-[A-Za-z0-9]\) [^\[].*/\1/' | \ - sed 's/\(^-[A-Za-z0-9] \[[A-Za-z0-9]*\]\) .*/\1/' | \ - sed 's/\(^--[A-Za-z0-9_-]*\) [^[].*/\1/' | \ - sed 's/\(^--[A-Za-z0-9_-]* \[[A-Za-z0-9]*\]\) [^[].*/\1/' | \ - tr '\n' ' ' | \ - sed 's/\[//g' | \ - sed 's/\]//g' | \ - sed 's/\(-[A-Za-z0-9]\)\( [^a-z]\)/\[\1\]\2/g' | \ - sed 's/\(--[A-Za-z0-9_-]*\)\( [^a-z]\)/\[\1\]\2/g' | \ - sed 's/\(-[A-Za-z0-9] [a-z]*\) /\[\1\] /g' | \ - sed 's/\(--[A-Za-z0-9_-]* [a-z]*\) /\[\1\] /g')" - -synopsis="$name $reqoptions $options [-h -v]"; - -# Preview the whole thing first. -#"$exec" -h | \ -# awk -v syn="$synopsis" \ -# '{ if (NR == 1) print "NAME\n '$name' - "tolower($0)"\nSYNOPSIS\n "syn" \nDESCRIPTION\n" ; else print } ' | \ -# sed '/^[^ ]/ y/qwertyuiopasdfghjklzxcvbnm:/QWERTYUIOPASDFGHJKLZXCVBNM /' | \ -# txt2man -T -P mlpack -t $name -d 1 - -# Now do it. -# The awk script is a little ugly, but it is meant to format parameters -# correctly so that the entire description of the parameter is on one line (this -# helps avoid 'man' warnings). -# The sed line at the end removes accidental macros from the output, replacing -# single-quotes at the beginning of a line with the troff escape code \(aq. -"$exec" -h | \ - sed 's/^For further information/Additional Information\n\n For further information/' | \ - sed 's/^consult the documentation/ consult the documentation/' | \ - sed 's/^distribution of mlpack./ distribution of mlpack./' | \ - awk -v syn="$synopsis" \ - '{ if (NR == 1) print "NAME\n '"$name"' - "tolower($0)"\nSYNOPSIS\n "syn" \nDESCRIPTION\n" ; else print } ' | \ - sed '/^[^ ]/ y/qwertyuiopasdfghjklzxcvbnm:/QWERTYUIOPASDFGHJKLZXCVBNM /' | \ - sed 's/ / /g' | \ - awk '/NAME/,/.*OPTIONS/ { if (!/.*OPTIONS/) { print; } } /ADDITIONAL INFORMATION/,0 { print; } /.*OPTIONS/,/ADDITIONAL INFORMATION/ { if (!/REQUIRED INPUT OPTIONS/ && !/OPTIONAL INPUT OPTIONS/ && !/OPTIONAL OUTPUT OPTIONS/ && !/ADDITIONAL INFORMATION/) { if (/ --/) { printf "\n" } sub(/^[ ]*/, ""); sub(/ [ ]*/, " "); printf "%s ", $0; } else { if (!/ADDITIONAL INFORMATION/) { print "\n"$0; } } }' | \ - sed 's/ ADDITIONAL INFORMATION/\n\nADDITIONAL INFORMATION/' | \ - txt2man -t "$name" -s 1 -r "mlpack-$version" -v "User Commands" | \ - sed "s/^'/\\\\(aq/" > "$output" - diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh deleted file mode 100644 index 5efe1fa57b..0000000000 --- a/scripts/build-docs.sh +++ /dev/null @@ -1,432 +0,0 @@ -#!/usr/bin/env bash -# -# Convert all the Markdown files in doc/ to HTML. -# This requires `kramdown` to be available on the path. -# `tidy` and `checklink` (from Debian's w3c-linkchecker package) are used to -# test the output and must also be available and on the path. -# Run this from the root directory of the repository. -# The output directory can be specified as the first option. - -if [ "$#" -gt 1 ]; then - echo "Usage: $0 [output_dir/]"; - exit 1; -elif [ "$#" -eq 1 ]; then - output_dir=$1; -else - output_dir=doc/html; -fi - -# If the header and footer already exist, they will not be overwritten. -template_html_header="${output_dir}/template.html.header"; -template_html_footer="${output_dir}/template.html.footer"; -template_html_sidebar="${output_dir}/template.html.sidebar"; - -if ! command -v kramdown &>/dev/null -then - echo "kramdown not installed! Cannot build documentation."; - exit 1; -fi - -if ! command -v tidy &>/dev/null -then - echo "tidy not installed! Cannot build documentation."; - exit 1; -fi - -if ! command -v checklink &>/dev/null -then - echo "checklink not installed! Cannot build documentation."; - exit 1; -fi - -if [ ! -d doc/ ]; -then - echo "Run this script from the root of the mlpack repository."; - exit 1; -fi - -# Define utility function to run kramdown and turn an .md file to an .html file. -run_kramdown() -{ - input_file=$1; - # This converts, e.g., ./doc/user/index.md -> doc/html/user/index.html. - tmp=${input_file#./doc/}; # Strip leading ./doc/. - output_file="$output_dir/${tmp%.md}.html"; - - # Determine what the link root is. If we're in the root directory, it's - # nothing, otherwise it's one of more '../'s. - dir_name=$(dirname $tmp); - link_root=""; - if [[ "$dir_name" != "." ]]; - then - levels_below_root=`echo $dir_name | awk -F'/' '{ print NF }'`; - link_root=$(printf '../%.0s' `seq 1 $levels_below_root`); - fi - - # Make the enclosing directory if needed. - out_dir=`dirname "$output_file"`; - mkdir -p "$out_dir"; - - # Kramdown doesn't detect languages correctly with the "```" fence; instead it - # needs the "~~~" fence. - sed 's/^```/~~~/' $input_file > $input_file.tmp; - - # Our documentation is full of relative links, like - # [name](other_file.md#anchor). We need these to turn into links to the - # rendered HTML file, like [name](other_file.html#anchor). We'll do this with - # regular expressions... - # - # - Note that this assumes there are no spaces in any filenames. - # - We also only catch the second part of the link '](' because the name of - # the link could be spread on multiple lines. - # - # We start by trying to catch the special cases README.md and HISTORY.md, - # which our documentation puts in a slightly different place. In addition, - # because those files are being moved to the root of the documentation, we - # must adjust links differently. - if [[ $input_file != "README.md" ]] && [[ $input_file != "HISTORY.md" ]]; - then - sed -i "s|\]([./]*README.md)|](${link_root}README.html)|g" $input_file.tmp; - sed -i "s|\]([./]*README.md#[0-9]-\([^ ]*\))|](${link_root}README.html#\1)|g" $input_file.tmp; - sed -i 's/\](\([^ ]*\).md)/](\1.html)/g' $input_file.tmp; - sed -i 's/\](\([^ ]*\).md#\([^ ]*\))/](\1.html#\2)/g' $input_file.tmp; - else - sed -i 's/\](doc\/\([^ ]*\).md)/](\1.html)/g' $input_file.tmp; - sed -i 's/\](doc\/\([^ ]*\).md#\([^ ]*\))/](\1.html#\2)/g' $input_file.tmp; - - # The README specifically has a link to GOVERNANCE.md, but we want to - # preserve that. We're not building that file into Markdown. - sed -i 's|(./GOVERNANCE.md)|(https://github.com/mlpack/mlpack/blob/master/GOVERNANCE.md)|' $input_file.tmp; - - # Ugh! Github naming of anchors is different than kramdown, and so we have - # to adjust all the table-of-contents anchor links in the README (and in - # that file only). - sed -i 's/\](#[0-9][0-9]-\([^ ]*\))/](#\1)/g' $input_file.tmp; - sed -i 's/\](#[0-9]-\([^ ]*\))/](#\1)/g' $input_file.tmp; - sed -i 's/\](#[0-9][0-9]\([^ ]*\))/](#\1)/g' $input_file.tmp; - sed -i 's/\](#[0-9]\([^ ]*\))/](#\1)/g' $input_file.tmp; - - # For HISTORY.md, we want to turn all references to Github issues into - # actual links, and all references to Github usernames into links to their - # profile. - if [[ $input_file == "HISTORY.md" ]]; - then - sed -i 's/#\([0-9][0-9]*\)/[#\1](https:\/\/github.com\/mlpack\/mlpack\/issues\/\1)/g' $input_file.tmp; - sed -i 's/\([^`]\)@\([a-zA-Z0-9_-][a-zA-Z0-9_-]*\)/\1[@\2](https:\/\/github.com\/\2)/g' $input_file.tmp; - fi - fi - - # Replace any links to source files with a link to the current version of the - # source file on Github. - sed -i 's/\](\/src\/\([^ ]*\)\.hpp)/](https:\/\/github.com\/mlpack\/mlpack\/blob\/master\/src\/\1.hpp)/' $input_file.tmp; - - # If this is binding documentation or quickstart documentation, don't set the - # default language to C++. - set_lang=1; - if [[ `dirname $input_file` == "./doc/user/bindings" ]]; - then - set_lang=0; - elif [[ `dirname $input_file` == "./doc/quickstart" ]]; - then - if [[ `basename $input_file .md` != "cpp" ]]; - then - set_lang=0; - fi - elif [[ $input_file == "HISTORY.md" ]]; - then - set_lang=0; - fi - - if [[ "$set_lang" == "0" ]]; - then - kramdown \ - -x parser-gfm \ - --syntax-highlighter rouge \ - --auto_ids \ - $input_file.tmp > "$output_file.tmp" || exit 1; - else - kramdown \ - -x parser-gfm \ - --syntax-highlighter rouge \ - --syntax-highlighter-opts '{ default_lang: c++ }' \ - --auto_ids \ - $input_file.tmp > "$output_file.tmp" || exit 1; - fi - cat "$template_html_header" | sed "s|LINKROOT|$link_root|" > "$output_file"; - - # Create the sidebar. Extract anchors from the page, unless we are looking at - # index.md, since the permanent part of the sidebar links all over index.md - # anyway. If we are looking at binding documentation, use a slightly - # different sidebar. - if { [[ $dir_name != "user/bindings" ]] && \ - [[ $dir_name != "quickstart" ]] } || - [[ $input_file == "./doc/quickstart/cpp.md" ]]; - then - cat "$template_html_sidebar" | sed "s|LINKROOT|$link_root|" \ - >> "$output_file"; - create_page_sidebar_section "$output_file.tmp" "$output_file" "$dir_name"; - else - echo "Using custom sidebar..."; - cat "$template_html_sidebar" | sed "s|LINKROOT|$link_root|" |\ - sed 's|
|
|' |\ - sed 's|
|
|' \ - >> "$output_file"; - # Some pages may have a custom sidebar HTML file. (Specifically, - # generated language bindings.) - if [[ $dir_name == "user/bindings" ]]; - then - cat "${input_file/%.md/.sidebar.html}" | sed "s|LINKROOT|$link_root|" \ - >> "$output_file"; - else - sidebar_file=`basename $input_file .md`.sidebar.html; - cat "./doc/user/bindings/$sidebar_file" | sed "s|LINKROOT|$link_root|" \ - >> "$output_file"; - fi - fi - - # Add clickable anchors to h2 and h3 headers. - echo "
" >> "$output_file"; - sed -E 's//🔗<\/a> /' "$output_file.tmp" >> "$output_file"; - - # Simple postprocessing to make tidy a little happier. - # (Muting the warning won't change the error code!) - sed -i 's//
/' "$output_file"; - - cat "$template_html_footer" >> "$output_file"; - rm -f $input_file.tmp "$output_file.tmp"; -} - -# Create the template header file. -create_template_header() -{ - output_file="$1"; - - # Note that LINKROOT will be substituted into place by run_kramdown. - cat > "$output_file" << EOF - - - - - - - - mlpack documentation - - -EOF -} - -# Create the template footer. -create_template_footer() -{ - output_file="$1"; - - cat > "$output_file" << EOF - - - -EOF -} - -# Extract anchors to build a sidebar. -# This should take the input HTML (before anchor elements are added), and it -# appends a sidebar list to the output HTML. -create_page_sidebar_section() -{ - sb_input_file="$1"; - sb_output_file="$2"; - sb_dir_name="$3"; # The directory containing the documentation. - sb_input_file_base=`basename "$sb_input_file" .html.tmp`; - - # Extract h2/h3 anchors into a list. For individual method documentation, we - # only extract h3 anchors because those use h2s as their headings. And, for - # core.md, we want to extract both h2 and h3 anchors. - if [[ "$sb_dir_name" == "user/methods" ]]; - then - # The page title on individual methods is encoded as an h2. - page_title=`grep '

\(.*\)<\/h2>/\1/'`; - - grep '

\(.*\)<\/h3>/
  • \2<\/a><\/li>/' > "$sb_output_file.side.tmp"; - elif [[ "$sb_input_file_base" == "core" ]]; - then - # The page title on the core class documentation page is encoded as an h1. - page_title=`grep '

    \(.*\)<\/h1>/\1/'`; - - # We want to collect h2s and h3s as individual documentation; each h2 should - # have a summary/details block. This is a little tedious to create... we'll - # do this by creating a temporary tab-separated file with lines like - # - # h2 anchor_name Anchor Title - # h3 anchor_name Anchor Title - # ... - # - # and then we'll construct the actual sidebar using that list. - grep '\(.*\)<\/h[23]>/\1\t\2\t\3/' \ - > "$sb_output_file.side.list.tmp"; - in_block=0; - while read line; do - # First, extract the pieces of each line. - line_type=`echo "$line" | awk -F'\t' '{ print $1 }'`; - anchor_name=`echo "$line" | awk -F'\t' '{ print $2 }'`; - anchor_title=`echo "$line" | awk -F'\t' '{ print $3 }'`; - - # For an h2, we have to print a summary block. - # Note that this assumes that *all* h2s have h3 children. If that's not - # true, some extra processing will be needed. - if [ "$line_type" = "h2" ]; - then - if [ "$in_block" = "1" ]; - then - # We have to close the previous block. - echo "

  • " >> "$sb_output_file.side.tmp"; - fi - - # Create the new details block. - echo "
  • " >> "$sb_output_file.side.tmp"; - echo "" >> "$sb_output_file.side.tmp"; - echo "$anchor_title" >> "$sb_output_file.side.tmp"; - echo "" >> "$sb_output_file.side.tmp"; - echo "" >> "$sb_output_file.side.tmp"; - echo "
  • " >> "$sb_output_file.side.tmp"; - fi - - rm -f "$sb_output_file.side.list.tmp"; - else - # On other pages, the page title is encoded as an h1. - page_title=`grep '

    \(.*\)<\/h1>/\1/'`; - - grep '

    \(.*\)<\/h2>/
  • \2<\/a><\/li>/' \ - > "$sb_output_file.side.tmp"; - fi - lines=`cat "$sb_output_file.side.tmp" | wc -l`; - - echo "" >> "$sb_output_file"; - echo "" >> "$sb_output_file"; - - rm -f "$sb_output_file.side.tmp"; -} - -# Save any existing template. -if [ -f "$template_html_header" ]; -then - mv "$template_html_header" template.html.header.tmp; -fi - -if [ -f "$template_html_footer" ]; -then - mv "$template_html_footer" template.html.footer.tmp; -fi - -rm -rf "$output_dir"; -mkdir -p "$output_dir"; -cp -v doc/css/* "$output_dir"; -mkdir -p "$output_dir/img/"; -cp -v doc/img/* "$output_dir/img/"; -mkdir -p "$output_dir/tutorials/res/"; -cp -v doc/tutorials/res/* "$output_dir/tutorials/res/"; - -# Create the template files we will use, if they don't already exist. -if [ -f template.html.header.tmp ]; -then - mv template.html.header.tmp "$template_html_header"; -else - create_template_header "$template_html_header"; - del_header=1; -fi - -if [ -f template.html.footer.tmp ]; -then - mv template.html.footer.tmp "$template_html_footer"; -else - create_template_footer "$template_html_footer"; - del_footer=1; -fi - -cp doc/sidebar.html "$template_html_sidebar"; - -# Process all the .md files. -for f in README.md HISTORY.md `find ./doc/ -iname '*.md'`; -do - # Skip the JOSS paper... - if [[ $f == *"joss_paper"* ]]; then - continue; - fi - - echo "Processing $f..."; - run_kramdown $f; - - # This converts, e.g., ./doc/user/index.md -> doc/html/user/index.html. - tmp=${f#./doc/}; # Strip leading ./doc/. - of="$output_dir/${tmp%.md}.html"; - - tidy -qe "$of" || exit 1; -done - -# Now take a second pass to check all the links. -find "$output_dir" -iname '*.html' -print0 | while read -d $'\0' f -do - echo "Checking links in $f..."; - - # To run checklink we have to strip out some perl stderr warnings... - checklink -qs \ - --follow-file-links \ - --suppress-broken 405 \ - --suppress-broken 301 \ - -X "https://eigen.tuxfamily.org/index.php\?title=Main_Page" \ - -X "https://mlpack.slack.com/" "$f" 2>&1 | - grep -v 'Use of uninitialized value' > checklink_out; - if [ -s checklink_out ]; - then - cat checklink_out; - exit 1; - fi - rm -f checklink_out; -done - -# Remove temporary files. -if [ "a$del_header" == "a1" ]; -then - rm -f "$template_html_header"; -fi - -if [ "a$del_footer" == "a1" ]; -then - rm -f "$template_html_footer"; -fi diff --git a/scripts/check-markdown-docs.sh b/scripts/check-markdown-docs.sh deleted file mode 100644 index 11c0d2993a..0000000000 --- a/scripts/check-markdown-docs.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash -# -# This script checks to ensure that generated Markdown documentation is the same -# as what's committed to the repository. After configuring mlpack with -# -DBUILD_MARKDOWN_BINDINGS=ON, use this simple script to detect changes in the -# documentation that should be committed. -# -# The only argument of the script is to the build directory. -if [ "$#" -ne 1 ]; -then - echo "Usage: $0 build/"; - echo " (replace build/ with your build directory, where you already" - echo " ran 'make markdown')"; - exit 1; -fi - -build_dir="$1"; - -# Check that Markdown documentation has been built. -if [[ ! -d "$build_dir/doc/" ]]; -then - echo "$build_dir/doc/ does not exist!"; - echo "Did you run 'make markdown' in your build directory ($build_dir)?"; - exit 1; -fi - -# Now check every file in the main repository bindings. -for f in doc/user/bindings/*; -do - echo "Checking $f..."; - f_base=`basename $f`; - - if [[ ! -f "$build_dir/doc/$f_base" ]]; - then - echo "$build_dir/doc/$f_base does not exist!"; - echo "Did you run 'make markdown' in your build directory ($build_dir)?"; - echo "Or does the file need to be removed from the repository?"; - exit 1; - fi - - diff -Nau $f "$build_dir/doc/$f_base"; - - if [ "$?" -ne 0 ]; - then - echo ""; - echo ""; - echo "Files $f and $build_dir/doc/$f_base differ! (See above.)"; - echo ""; - echo "If the sidebar differs, be sure to check if updates are needed in "; - echo "quickstart/*.sidebar.html!"; - exit 1; - fi -done diff --git a/scripts/release-mlpack.sh b/scripts/release-mlpack.sh deleted file mode 100644 index 2092df3bcf..0000000000 --- a/scripts/release-mlpack.sh +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/env bash -# -# Release a new version of mlpack. -# -# Usage: release-mlpack.sh github_username X Y Z -# -# where X is the major version, Y is the minor version, and Z is the patch -# version. Run this from the root of the repository. -# -# Make sure HISTORY.md is updated first! -set +e - -if [ "$#" -ne "4" ]; -then - echo "Usage: mlpack-release.sh "; - exit 1; -fi - -# First, check for any unlicensed files. -output=$( - for i in $(find src/ -iname '*.[hc]pp'); - do - echo -n $i": "; - cat $i | grep 'mlpack is free software;' | wc -l; - done |\ - grep -v ': 1' |\ - grep -v 'arma_extend' |\ - grep -v 'core/cereal' |\ - grep -v 'std_backport' |\ - grep -v 'arma_config.hpp' |\ - grep -v 'gitversion.hpp' |\ - grep -v 'CLI11.hpp' |\ - grep -v 'bindings/R/mlpack/src/boost/serialization' |\ - grep -v 'tests/catch.hpp'); -lines=`echo $output | grep -v '^[ ]*$' | wc -l`; - -if [ "0$lines" -gt "0" ]; -then - echo "Unlicensed files found! Aborting release."; - echo "$output"; - exit 1; -fi - -# Now, check that there are no local changes. -lines=`git diff | wc -l | sed -e 's/^\s*//g'`; -if [ "$lines" != "0" ]; then - echo "git diff returned a nonzero result!"; - echo ""; - git diff; - exit 1; -fi - -# Next, make sure the origin is right. -dest_remote_name=`git remote -v |\ - grep "mlpack/mlpack (fetch)" |\ - head -1 |\ - awk -F' ' '{ print $1 }'`; - -if [ "a$dest_remote_name" == "a" ]; then - echo "No git remote found for https://github.com/mlpack/mlpack!"; - echo "Make sure that you've got the ensmallen repository as a remote, and" \ - "that the master branch from that remote is checked out."; - echo "You can do this with a fresh repository via \`git clone" \ - "https://github.com/mlpack/mlpack\`."; - exit 1; -fi - -# Also check that we're on the master branch, from the correct origin. -current_branch=`git branch --no-color | grep '^\* ' | awk -F' ' '{ print $2 }'`; -current_origin=`git rev-parse --abbrev-ref --symbolic-full-name @{u} |\ - awk -F'/' '{ print $1 }'`; -if [ "a$current_branch" != "amaster" ]; then - echo "Current branch is $current_branch."; - echo "This script has to be run from the master branch."; - exit 1; -elif [ "a$current_origin" != "a$dest_remote_name" ]; then - echo "Current branch does not track from remote mlpack repository!"; - echo "Instead, it tracks from $current_origin/master."; - echo "Make sure to check out a branch that tracks $dest_remote_name/master."; - exit 1; -fi - -# Make sure `hub` is installed. -hub_output="`which hub`" || true; -if [ "a$hub_output" == "a" ]; then - echo "The Hub command-line tool must be installed for this script to run" \ - "successfully."; - echo "See https://hub.github.com for more details and installation" \ - "instructions."; - echo ""; - echo "(apt-get install hub on Debian and Ubuntu)"; - echo "(brew install hub via Homebrew)"; - exit 1; -fi - -# Check git remotes: we need to make sure we have a fork to push to. -github_user=$1; -remote_name=`git remote -v |\ - grep "$github_user/mlpack (push)" |\ - head -1 |\ - awk -F' ' '{ print $1 }'`; -if [ "a$remote_name" == "a" ]; then - echo "No git remote found for $github_user/mlpack!"; - echo "Adding remote '$github_user'."; - git remote add $github_user https://github.com/$github_user/mlpack; - remote_name="$github_user"; -fi -git fetch $github_user; - -# Make sure everything is up to date. -git pull; - -# Make updates to files that will be needed for the release. -MAJOR="$2"; -MINOR="$3"; -PATCH="$4"; - -# Update version. -sed --in-place 's/MLPACK_VERSION_MAJOR [0-9]*$/MLPACK_VERSION_MAJOR '$MAJOR'/' \ - src/mlpack/core/util/version.hpp; -sed --in-place 's/MLPACK_VERSION_MINOR [0-9]*$/MLPACK_VERSION_MINOR '$MINOR'/' \ - src/mlpack/core/util/version.hpp; -sed --in-place 's/MLPACK_VERSION_PATCH [0-9]*$/MLPACK_VERSION_PATCH '$PATCH'/' \ - src/mlpack/core/util/version.hpp; - -sed --in-place 's/mlpack-[0-9]\.[0-9]\.[0-9]/mlpack-'$MAJOR'.'$MINOR'.'$PATCH'/g' \ - doc/user/sample_ml_app.md; -sed --in-place 's/mlpack-[0-9]\.[0-9]\.[0-9]/mlpack-'$MAJOR'.'$MINOR'.'$PATCH'/g' \ - doc/examples/sample-ml-app/README.txt; -sed --in-place 's/mlpack-[0-9]\.[0-9]\.[0-9]/mlpack-'$MAJOR'.'$MINOR'.'$PATCH'/g' \ - doc/examples/sample-ml-app/sample-ml-app/sample-ml-app.vcxproj; - -sed --in-place 's/mlpack-[0-9]\.[0-9]\.[0-9]/mlpack-'$MAJOR'.'$MINOR'.'$PATCH'/g' \ - README.md; -sed --in-place 's/([0-9]\.[0-9]\.[0-9])/('$MAJOR'.'$MINOR'.'$PATCH')/g' \ - README.md; -sed --in-place 's/mlpack [0-9]\.[0-9]\.[0-9]/mlpack '$MAJOR'.'$MINOR'.'$PATCH'/g' \ - README.md; - -sed --in-place 's/## mlpack ?[.]?[.]?/## mlpack '$MAJOR'.'$MINOR'.'$PATCH'/g' HISTORY.md; -year=`date +%Y`; -month=`date +%m`; -day=`date +%d`; -sed --in-place 's/_????-??-??_/_'$year'-'$month'-'$day'_/g' \ - HISTORY.md; - -# Get the latest release of ensmallen. -git clone https://github.com/mlpack/ensmallen /tmp/ensmallen; -cd /tmp/ensmallen; -ens_ver=`git describe --tags $(git rev-list --tags --max-count=1)`; -echo "Latest version of ensmallen: $ens_ver" -cd -; -sed --in-place "s/ensmallen-latest.tar.gz/ensmallen-$ens_ver.tar.gz/" CMakeLists.txt; -rm -rf /tmp/ensmallen; - -# Make these changes on a release branch. -git checkout -b release-$MAJOR.$MINOR.$PATCH; - -git add src/mlpack/core/util/version.hpp \ - doc/user/sample_ml_app.md \ - doc/examples/sample-ml-app/README.txt \ - doc/examples/sample-ml-app/sample-ml-app/sample-ml-app.vcxproj \ - CMakeLists.txt \ - README.md \ - HISTORY.md; - -git commit -m "Update and release version $MAJOR.$MINOR.$PATCH."; - -changelog_str=`cat HISTORY.md |\ - awk '/^## /{f=0} /^## mlpack '"$MAJOR"'.'"$MINOR"'.'"$PATCH"'/{f=1} f{print}' |\ - grep -v '^#' |\ - grep -v '^_' |\ - tr '\n' '!' |\ - sed -e 's/! [ ]*/ /g' |\ - tr '!' '\n'`; -echo "Changelog string:" -echo "$changelog_str" - -# Update version again and add a new block for HISTORY.md. -sed --in-place 's/MLPACK_VERSION_PATCH [0-9]*$/MLPACK_VERSION_PATCH '$(($PATCH + 1))'/' \ - src/mlpack/core/util/version.hpp; -sed --in-place 's/ensmallen-'$ens_ver'.tar.gz/ensmallen-latest.tar.gz/' CMakeLists.txt; - -echo "# mlpack changelog" > HISTORY.md.new; -echo "" >> HISTORY.md.new; -echo "## mlpack ?.?.?" >> HISTORY.md.new; -echo "" >> HISTORY.md.new; -echo "_????-??-??_" >> HISTORY.md.new; -echo "" >> HISTORY.md.new; -cat HISTORY.md | grep -v '^# mlpack changelog' >> HISTORY.md.new; -mv HISTORY.md.new HISTORY.md; - -git add HISTORY.md; -git add src/mlpack/core/util/version.hpp CMakeLists.txt; - -git commit -m "Add new block for next release to HISTORY.md."; - -# Push to new branch. -git push --set-upstream $github_user release-$MAJOR.$MINOR.$PATCH; - -# Next, we have to actually open the PR for the release. -hub pull-request \ - -b mlpack:master \ - -h $github_user:release-$MAJOR.$MINOR.$PATCH \ - -m "Release version $MAJOR.$MINOR.$PATCH" \ - -m "This automatically-generated pull request adds the commits necessary to make the $MAJOR.$MINOR.$PATCH release." \ - -m "Once the PR is merged, mlpack-bot will tag the release as HEAD~1 (so that it doesn't include the new HISTORY block) and publish it." \ - -m "When you merge this PR, be sure to merge it using a *rebase*." \ - -m "### Changelog" \ - -m "$changelog_str" \ - -l "t: release" - -echo ""; -echo "Switching back to 'master' branch."; -echo "If you want to access the release branch again, use \`git checkout " \ - "release-$MAJOR.$MINOR.$PATCH\`."; -git checkout master; -echo 0; diff --git a/scripts/test-docs.sh b/scripts/test-docs.sh deleted file mode 100644 index a787f04362..0000000000 --- a/scripts/test-docs.sh +++ /dev/null @@ -1,407 +0,0 @@ -#!/usr/bin/env bash -# -# Extract C++ code blocks from either an individual Markdown file or a directory -# full of Markdown files. This does roughly what you would expect it to, but -# there is a little bit of magic: -# -# * All ```c++ code blocks are extracted into their own .cpp files. -# -# * mlpack.hpp is included in each file, and each code block is placed in an -# `int main() { }` block. -# -# * If Eigen or xtensor is detected, correct includes are added. -# -# * All data referenced in http URLs is downloaded. -# -# * If a code block is just a class declaration, it will be added to the *next* -# code block file. -# -# Once all code blocks are compiled, they are run just to make sure they run -# correctly. If a single file was passed, all inputs and outputs are printed; -# if an entire directory is given, program output is only printed on error. - -if [[ $# -ne 1 ]]; -then - echo "Usages: " >&2; - echo " - $0 input_file.md" >&2; - echo " - $0 markdown_directory/" >&2; - exit 2; -fi - -if [ -z "$CXX" ]; -then - echo "You must set \$CXX to the compiler you want to use!" >&2; - exit 1; -fi - -if [ -z "$CXXFLAGS" ]; -then - echo "Warning: \$CXXFLAGS is unset. If ensmallen, Armadillo, STB, or cereal "; - echo " are not in standard locations, builds will fail! Be sure to use "; - echo " absolute paths, not relative paths."; -fi - -if [ -z "$LDFLAGS" ]; -then - echo "Warning: \$LDFLAGS is unset. If libarmadillo.so is not in a standard "; - echo " location, builds will fail!"; -fi - -# First determine what files we are looking at. -if [ -d $1 ]; -then - files=`find $1 -iname '*.md'`; - mode="directory"; -else - files=$1; - mode="file"; -fi - -# Extract the C++ code blocks from a particular file, creating -# $output_prefix1.cpp, $output_prefix2.cpp, and so on and so forth. -# -# The code in those snippets will be placed into an int main() { } block, and -# mlpack.hpp will be included. -extract_code_blocks() -{ - input_file=$1; - output_prefix=$2; - - # Extract into temporary files. - sed -n '/^```c++/,/^```/ p' < $input_file > $input_file.tmp; - output_file_id=0; - output_file_display="00"; # Hopefully no file has more than 100 examples... - - # Track whether or not the last line was a fence, since we get them two at a - # time. We initially set this to 1, because the first fence does not have a - # preceding fence close above it. - last_line_fence=1; - - # Track whether or not the entire last file corresponded to a class - # declaration. - class_decl=0; - - while IFS= read -r line; - do - if [[ $last_line_fence == 1 ]]; - then - # Skip this line---it will be a fence opening. - last_line_fence=0; - - # Create main() function to wrap the code in. - echo "#include " > $output_prefix$output_file_display.cpp; - echo "" >> $output_prefix$output_file_display.cpp; - - # If we have a class declaration from the previous file, insert it. - if [[ $class_decl == 1 ]]; - then - class_decl=0; - last_output_file_id=$(($output_file_id - 1)); - last_output_file_display=$(printf "%02d" $last_output_file_id); - - cat $output_prefix$last_output_file_display.cpp | awk ' - BEGIN { p=0 } - /int main()/ { p=1 } - /^{/ { if(p == 1) { p=2; o=1 } } - /^}/ { p=0; } - // { if (p == 2 && o == 0) { print substr($0, 3) } o=0 }' >> $output_prefix$output_file_display.cpp; - echo "" >> $output_prefix$output_file_display.cpp; - rm -f $output_prefix$last_output_file_display.cpp; - fi - - echo "int main()" >> $output_prefix$output_file_display.cpp; - echo "{" >> $output_prefix$output_file_display.cpp; - continue; - fi - - if [[ $line == '```'* ]]; - then - last_line_fence=1; - - # Close main() function. - echo "}" >> $output_prefix$output_file_display.cpp; - - # Check after the fact: was this file only a class declaration? If so, we - # want to put it instead into the next file. - has_class1=`grep '^ class' $output_prefix$output_file_display.cpp | wc -l`; - has_class2=`grep '^ };' $output_prefix$output_file_display.cpp | wc -l`; - if [[ "$has_class1" != "0" && "$has_class2" != "0" ]]; - then - class_decl=1; - fi; - - # Detect if we need any to add any special headers. We have to do this - # when we finish with the file... - if [[ `grep 'Eigen::' $output_prefix$output_file_display.cpp | wc -l` -gt 0 ]]; - then - sed -i '1s/^/#include \n/' $output_prefix$output_file_display.cpp; - fi - - if [[ `grep 'xt::' $output_prefix$output_file_display.cpp | wc -l` -gt 0 ]]; - then - sed -i '1s/^/#include \n/' $output_prefix$output_file_display.cpp; - sed -i '1s/^/#include \n/' $output_prefix$output_file_display.cpp; - fi - - output_file_id=$(($output_file_id + 1)); - output_file_display=$(printf "%02d" $output_file_id); - - continue; - fi - - # Include indentation (two spaces). - echo " $line" >> $output_prefix$output_file_display.cpp; - done < $input_file.tmp; - - # The last file is always invalid---we opened it without knowing whether - # anything would be in it. - rm -f $output_prefix$output_file_display.cpp; - - # Check the "true" last file: if it's only class declarations, no need to - # compile it. - output_file_id=$(($output_file_id - 1)); - output_file_display=$(printf "%02d" $output_file_id); - if [ -f $output_prefix$output_file_display.cpp ]; - then - cat $output_prefix$output_file_display.cpp | awk ' - BEGIN { p=0 } - /int main()/ { p=1 } - /^{/ { if(p == 1) { p=2; o=1 } } - /^}/ { p=0 } - // { if (p == 2 && o == 0) { print substr($0, 3) } o=0 }' >> $output_prefix$output_file_display.cpp.tmp; - has_class1=`grep '^class' $output_prefix$output_file_display.cpp.tmp | wc -l`; - has_class2=`grep '^};' $output_prefix$output_file_display.cpp.tmp | wc -l`; - if [[ "$has_class1" != "0" && "$has_class2" != "0" ]]; - then - # The file's main() function is just a class declaration. Nuke it. - rm -f $output_prefix$output_file_display.cpp; - fi - rm -f $output_prefix$output_file_display.cpp.tmp; - fi - - rm -f $input_file.tmp; -} - -compile_code_blocks() -{ - input_dir=$1; - - # If there are no files to compile, leave early. - if ! compgen -G $input_dir/*.cpp >/dev/null; - then - return; - fi - - for f in $input_dir/*.cpp; - do - echo " Compiling $f..."; - of=${f%.cpp}; - - if ! $CXX -std=c++17 -Isrc/ $CXXFLAGS -o $of $f $LDFLAGS -larmadillo 2>$of.tmp; - then - echo "Compilation of the following program failed:"; - echo ""; - cat $f; - echo ""; - echo "First ten lines of error output:"; - head $of.tmp; - echo ""; - echo "For full error output run either:"; - echo " - less $of.tmp"; - echo " - $CXX -std=c++17 -Isrc/ $CXXFLAGS -o $of $f $LDFLAGS -larmadillo"; - echo ""; - echo "Did you set \$CXX, \$CXXFLAGS, and \$LDFLAGS correctly?" - exit 1; - fi - done -} - -download_http_artifacts() -{ - input_dir=$1; - output_dir=$2; - - # Get a list of all HTTP resources. - artifacts=`grep 'http[s]*://' $input_dir/*.cpp |\ - sed 's/^.*\(http[^ ]*\).*$/\1/' |\ - sort |\ - uniq |\ - grep 'csv\|arff\|bin\|png' |\ - sed 's/\.$//'`; - cd $output_dir; - for a in $artifacts; - do - out_a=`basename $a`; - if [ ! -f $out_a ]; - then - echo " Downloading $a..."; - if ! curl -s -O $a; - then - echo "Error downloading $a!"; - exit 1; - fi - fi - done - cd - >/dev/null; - - # Special case: if we are looking at core.md, this has two special files we - # need to create that is used in the example. - f=`basename $input_dir`; - if [[ "$f" == "core" || "$f" == "matrices" ]]; - then - cd $output_dir; - echo " Creating data.csv..."; - cat > data.csv << EOF -3,3,3,3,0 -3,4,4,3,0 -3,4,4,3,0 -3,3,4,3,0 -3,6,4,3,0 -2,4,4,3,0 -2,4,4,1,0 -3,3,3,2,0 -3,4,4,2,0 -3,4,4,2,0 -3,3,4,2,0 -3,6,4,2,0 -2,4,4,2,0 -EOF - - echo " Creating mixed_string_data.csv..."; - cat > mixed_string_data.csv << EOF -3,"hello",3,"f",0 -3,"goodbye",4,"f",0 -3,"goodbye",4,"e",0 -3,"hello",4,"d",0 -3,"hello",4,"d",0 -2,"hello",4,"d",0 -2,"hello",4,"d",0 -3,"goodbye",3,"f",0 -3,"goodbye",4,"f",0 -3,"hello",4,"f",0 -3,"hello",4,"c",0 -3,"hello",4,"f",0 -2,"hello",4,"c",0 -EOF - cd - >/dev/null; - fi -} - -run_code_blocks() -{ - input_dir=$1; - - for f in $input_dir/*.cpp; - do - f_exec=${f%.cpp}; - if [[ "$mode" == "directory" ]]; - then - echo " Running $f_exec..."; - if ! ./$f_exec 2>&1 >/dev/null; - then - echo " Error running $f_exec!"; - exit 1; - fi - else - echo " --------------------------------------------------------------------- "; - echo " Contents of $f:"; - echo ""; - cat $f; - echo ""; - echo " --------------------------------------------------------------------- "; - echo " Output of $f_exec:"; - echo ""; - - if ! ./$f_exec; - then - echo ""; - echo "Error running $f_exec! See output above."; - exit 1; - fi - echo ""; - echo " --------------------------------------------------------------------- "; - fi - done -} - -# Main loop: process the files we were asked to process. -mkdir -p doc/build/; -for f in $files; -do - if [[ "$mode" == "directory" ]]; - then - declare -a files_to_skip=( - # These files have small incomplete snippets that can't compile into - # standalone programs. - "sample_ml_app.md" - "hpt.md" - "cv.md" - "timer.md" - "bindings.md" - "elemtype.md" - "iodoc.md" - "kernels.md" - "metrics.md" - "trees.md" - # The tutorials are old and are likely to be replaced, so let's not test - # them. - "amf.md" - "ann.md" - "approx_kfn.md" - "cf.md" - "datasetmapper.md" - "det.md" - "emst.md" - "fastmks.md" - "image.md" - "kmeans.md" - "linear_regression.md" - "neighbor_search.md" - "range_search.md" - "reinforcement_learning.md" - "asynchronous_learning.md" - "ddpg.md" - "q_learning.md" - "sac.md" - "td3.md" - # Skip quickstarts, although we should eventually test them. - "cpp.md" - ); - - skip=0; - for skip_f in "${files_to_skip[@]}"; - do - base_f=`basename $f`; - if [ "$base_f" = "$skip_f" ]; - then - skip=1; - break; - fi - done - - if [[ $skip -eq 1 ]]; - then - continue; - fi - fi - - echo "Building documentation for $f..."; - - build_dir_tmp=${f#doc/}; - build_dir=${build_dir_tmp%.md}; - base_file=`basename $f .md`; - mkdir -p doc/build/$build_dir/; - - extract_code_blocks $f doc/build/$build_dir/$base_file; - # If there are no C++ files, don't do anything else.. - if ! compgen -G doc/build/$build_dir/*.cpp >/dev/null; - then - continue; - fi - - compile_code_blocks doc/build/$build_dir; - download_http_artifacts doc/build/$build_dir doc/build/; - cd doc/build/; - run_code_blocks $build_dir; - cd ../../; -done diff --git a/scripts/update-website-after-release.sh b/scripts/update-website-after-release.sh deleted file mode 100644 index e80ad8694f..0000000000 --- a/scripts/update-website-after-release.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env bash -# -# This script is used to update the website after an mlpack release is made. -# Push access to the mlpack.org website repository is needed. Generally, this -# script will be run by mlpack-bot, so it never needs to be run by hand. -# -# Usage: update-website-after-release.sh - -MAJOR=$1; -MINOR=$2; -PATCH=$3; - -# Make sure that the mlpack repository exists. -dest_remote_name=`git remote -v |\ - grep "mlpack/mlpack (fetch)" |\ - head -1 |\ - awk -F' ' '{ print $1 }'`; - -if [ "a$dest_remote_name" == "a" ]; then - echo "No git remote found for mlpack/mlpack!"; - echo "Make sure that you've got the mlpack repository as a remote, and" \ - "that the master branch from that remote is checked out."; - echo "You can do this with a fresh repository via \`git clone" \ - "https://github.com/mlpack/mlpack\`."; - exit 1; -fi - -# Update the checked out repository, so that we can get the tags. -git fetch $dest_remote_name; - -# Check out a copy of the ensmallen.org repository. -git clone git@github.com:mlpack/mlpack.org /tmp/mlpack.org/; - -# Create the release file. -git archive --prefix=mlpack-$MAJOR.$MINOR.$PATCH/ $MAJOR.$MINOR.$PATCH |\ - gzip > /tmp/mlpack.org/files/mlpack-$MAJOR.$MINOR.$PATCH.tar.gz; - -# Now update the website. -wd=`pwd`; -cd /tmp/mlpack.org/; - -# These may be specific to the old website. -sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' index.md; -sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' docs.md; -sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' getstarted.md; -sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' community.md; -git add index.md docs.md getstarted.md community.md; - -# These may be specific to the new website. -sed --in-place 's/mlpack-[0-9]\.[0-9]\.[0-9]/mlpack-'$MAJOR'.'$MINOR'.'$PATCH'/g' html/index.html; -sed --in-place 's/Version [0-9]\.[0-9]\.[0-9]/Version '$MAJOR'.'$MINOR'.'$PATCH'/g' html/index.html; -sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' html/getstarted.html; -sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' html/config/install.md; -git add html/index.html html/getstarted.html html/config/install.md; - -git commit -m "Update links to latest stable version."; - -git add files/mlpack-$MAJOR.$MINOR.$PATCH.tar.gz; -git commit -m "Release version $MAJOR.$MINOR.$PATCH."; - -# Finally, push, and we're done. -git push origin; -cd $wd; - -rm -rf /tmp/mlpack.org; From 40a03c7432e12abd5e756ad45a31b729fe989a42 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 26 Jun 2024 19:36:28 +0200 Subject: [PATCH 074/212] Restore accidentally deleted files and commit changes to dropout.hpp and dropout_impl.hpp --- CMake/allexec2man.sh | 26 ++ CMake/exec2man.sh | 104 ++++++ scripts/build-docs.sh | 432 ++++++++++++++++++++++++ scripts/check-markdown-docs.sh | 53 +++ scripts/release-mlpack.sh | 216 ++++++++++++ scripts/test-docs.sh | 407 ++++++++++++++++++++++ scripts/update-website-after-release.sh | 65 ++++ 7 files changed, 1303 insertions(+) create mode 100755 CMake/allexec2man.sh create mode 100755 CMake/exec2man.sh create mode 100755 scripts/build-docs.sh create mode 100755 scripts/check-markdown-docs.sh create mode 100755 scripts/release-mlpack.sh create mode 100755 scripts/test-docs.sh create mode 100755 scripts/update-website-after-release.sh diff --git a/CMake/allexec2man.sh b/CMake/allexec2man.sh new file mode 100755 index 0000000000..885ffe73da --- /dev/null +++ b/CMake/allexec2man.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +set -e + +if [ $# != 2 ]; then + echo "Convert all of the executables in this directory that are not tests to man" + echo "pages in the given directory." + echo + echo "Usage:" + echo " allexec2man.sh /full/path/of/exec2man.sh output_directory/" + echo + echo "For the executable 'cheese', the file 'cheese.1.gz' will be created in the" + echo "output directory." + exit 1 +fi + +exec2man="$1" +outdir="$2" + +mkdir -p "$outdir" +for program in $(find . -type f -perm -u+x -iname 'mlpack_*' | \ + grep -v '[.]$' | \ + grep -v '_test$'); do + echo "Generating man page for $program..."; + "$exec2man" "$program" "$outdir/$program.1" +done diff --git a/CMake/exec2man.sh b/CMake/exec2man.sh new file mode 100755 index 0000000000..3ca65b9bbc --- /dev/null +++ b/CMake/exec2man.sh @@ -0,0 +1,104 @@ +#!/bin/sh +# +# Convert the output of an mlpack executable into a man page. This assumes that +# the IO subsystem is used to output help, that the executable is properly +# documented, and that the program is run in the directory that the executable +# is in. Usually, this is used by CMake on Linux/UNIX systems to generate the +# man pages. +# +# Usage: +# exec2man.sh executable_name output_file_name +# +# No warranties... +# +# @author Ryan Curtin + +set -e + +if [ $# != 2 ]; then + echo "Generates man page from the help text of an mlpack utility program." + echo "Usage: $0 mlpack_executable generated-man-page.1" + exit 1 +fi + +exec="$1" +name="$(basename "$exec")" +output="$2" + +if [ "$name" = "$exec" ]; then + # if no directory prefix with explict ./ to avoid path search + exec="./$exec" +fi + +if [ ! -x "$exec" ]; then + echo "error: cannot find executable file $exec" + exit 1 +fi + +# Get the version. +version=$("$exec" --version | sed 's/^.* \([^ ]*\)\.$/\1/') + +# Generate the synopsis. +# First, required options. +reqoptions="$("$exec" --help | \ + awk '/Required input options:/,/Optional input options:/' | \ + grep '^ --' | \ + sed 's/^ --/--/' | \ + sed 's/^--[A-Za-z0-9_-]* (\(-[A-Za-z0-9]\))/\1/' | \ + sed 's/\(^-[A-Za-z0-9]\) [^\[].*/\1/' | \ + sed 's/\(^-[A-Za-z0-9] \[[A-Za-z0-9]*\]\) .*/\1/' | \ + sed 's/\(^--[A-Za-z0-9_-]*\) [^[].*/\1/' | \ + sed 's/\(^--[A-Za-z0-9_-]* \[[A-Za-z0-9]*\]\) [^[].*/\1/' | \ + tr '\n' ' ' | \ + sed 's/\[//g' | \ + sed 's/\]//g')" + +# Then, regular options. +options="$("$exec" -h | \ + awk '/Optional input options:/,/For further information,/' | \ + grep '^ --' | \ + sed 's/^ --/--/' | \ + grep -v -- '--help' | \ + grep -v -- '--info' | \ + grep -v -- '--verbose' | \ + sed 's/^--[A-Za-z0-9_-]* (\(-[A-Za-z0-9]\))/\1/' | \ + sed 's/\(^-[A-Za-z0-9]\) [^\[].*/\1/' | \ + sed 's/\(^-[A-Za-z0-9] \[[A-Za-z0-9]*\]\) .*/\1/' | \ + sed 's/\(^--[A-Za-z0-9_-]*\) [^[].*/\1/' | \ + sed 's/\(^--[A-Za-z0-9_-]* \[[A-Za-z0-9]*\]\) [^[].*/\1/' | \ + tr '\n' ' ' | \ + sed 's/\[//g' | \ + sed 's/\]//g' | \ + sed 's/\(-[A-Za-z0-9]\)\( [^a-z]\)/\[\1\]\2/g' | \ + sed 's/\(--[A-Za-z0-9_-]*\)\( [^a-z]\)/\[\1\]\2/g' | \ + sed 's/\(-[A-Za-z0-9] [a-z]*\) /\[\1\] /g' | \ + sed 's/\(--[A-Za-z0-9_-]* [a-z]*\) /\[\1\] /g')" + +synopsis="$name $reqoptions $options [-h -v]"; + +# Preview the whole thing first. +#"$exec" -h | \ +# awk -v syn="$synopsis" \ +# '{ if (NR == 1) print "NAME\n '$name' - "tolower($0)"\nSYNOPSIS\n "syn" \nDESCRIPTION\n" ; else print } ' | \ +# sed '/^[^ ]/ y/qwertyuiopasdfghjklzxcvbnm:/QWERTYUIOPASDFGHJKLZXCVBNM /' | \ +# txt2man -T -P mlpack -t $name -d 1 + +# Now do it. +# The awk script is a little ugly, but it is meant to format parameters +# correctly so that the entire description of the parameter is on one line (this +# helps avoid 'man' warnings). +# The sed line at the end removes accidental macros from the output, replacing +# single-quotes at the beginning of a line with the troff escape code \(aq. +"$exec" -h | \ + sed 's/^For further information/Additional Information\n\n For further information/' | \ + sed 's/^consult the documentation/ consult the documentation/' | \ + sed 's/^distribution of mlpack./ distribution of mlpack./' | \ + awk -v syn="$synopsis" \ + '{ if (NR == 1) print "NAME\n '"$name"' - "tolower($0)"\nSYNOPSIS\n "syn" \nDESCRIPTION\n" ; else print } ' | \ + sed '/^[^ ]/ y/qwertyuiopasdfghjklzxcvbnm:/QWERTYUIOPASDFGHJKLZXCVBNM /' | \ + sed 's/ / /g' | \ + awk '/NAME/,/.*OPTIONS/ { if (!/.*OPTIONS/) { print; } } /ADDITIONAL INFORMATION/,0 { print; } /.*OPTIONS/,/ADDITIONAL INFORMATION/ { if (!/REQUIRED INPUT OPTIONS/ && !/OPTIONAL INPUT OPTIONS/ && !/OPTIONAL OUTPUT OPTIONS/ && !/ADDITIONAL INFORMATION/) { if (/ --/) { printf "\n" } sub(/^[ ]*/, ""); sub(/ [ ]*/, " "); printf "%s ", $0; } else { if (!/ADDITIONAL INFORMATION/) { print "\n"$0; } } }' | \ + sed 's/ ADDITIONAL INFORMATION/\n\nADDITIONAL INFORMATION/' | \ + txt2man -t "$name" -s 1 -r "mlpack-$version" -v "User Commands" | \ + sed "s/^'/\\\\(aq/" > "$output" + diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh new file mode 100755 index 0000000000..5efe1fa57b --- /dev/null +++ b/scripts/build-docs.sh @@ -0,0 +1,432 @@ +#!/usr/bin/env bash +# +# Convert all the Markdown files in doc/ to HTML. +# This requires `kramdown` to be available on the path. +# `tidy` and `checklink` (from Debian's w3c-linkchecker package) are used to +# test the output and must also be available and on the path. +# Run this from the root directory of the repository. +# The output directory can be specified as the first option. + +if [ "$#" -gt 1 ]; then + echo "Usage: $0 [output_dir/]"; + exit 1; +elif [ "$#" -eq 1 ]; then + output_dir=$1; +else + output_dir=doc/html; +fi + +# If the header and footer already exist, they will not be overwritten. +template_html_header="${output_dir}/template.html.header"; +template_html_footer="${output_dir}/template.html.footer"; +template_html_sidebar="${output_dir}/template.html.sidebar"; + +if ! command -v kramdown &>/dev/null +then + echo "kramdown not installed! Cannot build documentation."; + exit 1; +fi + +if ! command -v tidy &>/dev/null +then + echo "tidy not installed! Cannot build documentation."; + exit 1; +fi + +if ! command -v checklink &>/dev/null +then + echo "checklink not installed! Cannot build documentation."; + exit 1; +fi + +if [ ! -d doc/ ]; +then + echo "Run this script from the root of the mlpack repository."; + exit 1; +fi + +# Define utility function to run kramdown and turn an .md file to an .html file. +run_kramdown() +{ + input_file=$1; + # This converts, e.g., ./doc/user/index.md -> doc/html/user/index.html. + tmp=${input_file#./doc/}; # Strip leading ./doc/. + output_file="$output_dir/${tmp%.md}.html"; + + # Determine what the link root is. If we're in the root directory, it's + # nothing, otherwise it's one of more '../'s. + dir_name=$(dirname $tmp); + link_root=""; + if [[ "$dir_name" != "." ]]; + then + levels_below_root=`echo $dir_name | awk -F'/' '{ print NF }'`; + link_root=$(printf '../%.0s' `seq 1 $levels_below_root`); + fi + + # Make the enclosing directory if needed. + out_dir=`dirname "$output_file"`; + mkdir -p "$out_dir"; + + # Kramdown doesn't detect languages correctly with the "```" fence; instead it + # needs the "~~~" fence. + sed 's/^```/~~~/' $input_file > $input_file.tmp; + + # Our documentation is full of relative links, like + # [name](other_file.md#anchor). We need these to turn into links to the + # rendered HTML file, like [name](other_file.html#anchor). We'll do this with + # regular expressions... + # + # - Note that this assumes there are no spaces in any filenames. + # - We also only catch the second part of the link '](' because the name of + # the link could be spread on multiple lines. + # + # We start by trying to catch the special cases README.md and HISTORY.md, + # which our documentation puts in a slightly different place. In addition, + # because those files are being moved to the root of the documentation, we + # must adjust links differently. + if [[ $input_file != "README.md" ]] && [[ $input_file != "HISTORY.md" ]]; + then + sed -i "s|\]([./]*README.md)|](${link_root}README.html)|g" $input_file.tmp; + sed -i "s|\]([./]*README.md#[0-9]-\([^ ]*\))|](${link_root}README.html#\1)|g" $input_file.tmp; + sed -i 's/\](\([^ ]*\).md)/](\1.html)/g' $input_file.tmp; + sed -i 's/\](\([^ ]*\).md#\([^ ]*\))/](\1.html#\2)/g' $input_file.tmp; + else + sed -i 's/\](doc\/\([^ ]*\).md)/](\1.html)/g' $input_file.tmp; + sed -i 's/\](doc\/\([^ ]*\).md#\([^ ]*\))/](\1.html#\2)/g' $input_file.tmp; + + # The README specifically has a link to GOVERNANCE.md, but we want to + # preserve that. We're not building that file into Markdown. + sed -i 's|(./GOVERNANCE.md)|(https://github.com/mlpack/mlpack/blob/master/GOVERNANCE.md)|' $input_file.tmp; + + # Ugh! Github naming of anchors is different than kramdown, and so we have + # to adjust all the table-of-contents anchor links in the README (and in + # that file only). + sed -i 's/\](#[0-9][0-9]-\([^ ]*\))/](#\1)/g' $input_file.tmp; + sed -i 's/\](#[0-9]-\([^ ]*\))/](#\1)/g' $input_file.tmp; + sed -i 's/\](#[0-9][0-9]\([^ ]*\))/](#\1)/g' $input_file.tmp; + sed -i 's/\](#[0-9]\([^ ]*\))/](#\1)/g' $input_file.tmp; + + # For HISTORY.md, we want to turn all references to Github issues into + # actual links, and all references to Github usernames into links to their + # profile. + if [[ $input_file == "HISTORY.md" ]]; + then + sed -i 's/#\([0-9][0-9]*\)/[#\1](https:\/\/github.com\/mlpack\/mlpack\/issues\/\1)/g' $input_file.tmp; + sed -i 's/\([^`]\)@\([a-zA-Z0-9_-][a-zA-Z0-9_-]*\)/\1[@\2](https:\/\/github.com\/\2)/g' $input_file.tmp; + fi + fi + + # Replace any links to source files with a link to the current version of the + # source file on Github. + sed -i 's/\](\/src\/\([^ ]*\)\.hpp)/](https:\/\/github.com\/mlpack\/mlpack\/blob\/master\/src\/\1.hpp)/' $input_file.tmp; + + # If this is binding documentation or quickstart documentation, don't set the + # default language to C++. + set_lang=1; + if [[ `dirname $input_file` == "./doc/user/bindings" ]]; + then + set_lang=0; + elif [[ `dirname $input_file` == "./doc/quickstart" ]]; + then + if [[ `basename $input_file .md` != "cpp" ]]; + then + set_lang=0; + fi + elif [[ $input_file == "HISTORY.md" ]]; + then + set_lang=0; + fi + + if [[ "$set_lang" == "0" ]]; + then + kramdown \ + -x parser-gfm \ + --syntax-highlighter rouge \ + --auto_ids \ + $input_file.tmp > "$output_file.tmp" || exit 1; + else + kramdown \ + -x parser-gfm \ + --syntax-highlighter rouge \ + --syntax-highlighter-opts '{ default_lang: c++ }' \ + --auto_ids \ + $input_file.tmp > "$output_file.tmp" || exit 1; + fi + cat "$template_html_header" | sed "s|LINKROOT|$link_root|" > "$output_file"; + + # Create the sidebar. Extract anchors from the page, unless we are looking at + # index.md, since the permanent part of the sidebar links all over index.md + # anyway. If we are looking at binding documentation, use a slightly + # different sidebar. + if { [[ $dir_name != "user/bindings" ]] && \ + [[ $dir_name != "quickstart" ]] } || + [[ $input_file == "./doc/quickstart/cpp.md" ]]; + then + cat "$template_html_sidebar" | sed "s|LINKROOT|$link_root|" \ + >> "$output_file"; + create_page_sidebar_section "$output_file.tmp" "$output_file" "$dir_name"; + else + echo "Using custom sidebar..."; + cat "$template_html_sidebar" | sed "s|LINKROOT|$link_root|" |\ + sed 's|
    |
    |' |\ + sed 's|
    |
    |' \ + >> "$output_file"; + # Some pages may have a custom sidebar HTML file. (Specifically, + # generated language bindings.) + if [[ $dir_name == "user/bindings" ]]; + then + cat "${input_file/%.md/.sidebar.html}" | sed "s|LINKROOT|$link_root|" \ + >> "$output_file"; + else + sidebar_file=`basename $input_file .md`.sidebar.html; + cat "./doc/user/bindings/$sidebar_file" | sed "s|LINKROOT|$link_root|" \ + >> "$output_file"; + fi + fi + + # Add clickable anchors to h2 and h3 headers. + echo "
  • /
    /' "$output_file"; + + cat "$template_html_footer" >> "$output_file"; + rm -f $input_file.tmp "$output_file.tmp"; +} + +# Create the template header file. +create_template_header() +{ + output_file="$1"; + + # Note that LINKROOT will be substituted into place by run_kramdown. + cat > "$output_file" << EOF + + + + + + + + mlpack documentation + + +EOF +} + +# Create the template footer. +create_template_footer() +{ + output_file="$1"; + + cat > "$output_file" << EOF + + + +EOF +} + +# Extract anchors to build a sidebar. +# This should take the input HTML (before anchor elements are added), and it +# appends a sidebar list to the output HTML. +create_page_sidebar_section() +{ + sb_input_file="$1"; + sb_output_file="$2"; + sb_dir_name="$3"; # The directory containing the documentation. + sb_input_file_base=`basename "$sb_input_file" .html.tmp`; + + # Extract h2/h3 anchors into a list. For individual method documentation, we + # only extract h3 anchors because those use h2s as their headings. And, for + # core.md, we want to extract both h2 and h3 anchors. + if [[ "$sb_dir_name" == "user/methods" ]]; + then + # The page title on individual methods is encoded as an h2. + page_title=`grep '

    \(.*\)<\/h2>/\1/'`; + + grep '

    \(.*\)<\/h3>/
  • \2<\/a><\/li>/' > "$sb_output_file.side.tmp"; + elif [[ "$sb_input_file_base" == "core" ]]; + then + # The page title on the core class documentation page is encoded as an h1. + page_title=`grep '

    \(.*\)<\/h1>/\1/'`; + + # We want to collect h2s and h3s as individual documentation; each h2 should + # have a summary/details block. This is a little tedious to create... we'll + # do this by creating a temporary tab-separated file with lines like + # + # h2 anchor_name Anchor Title + # h3 anchor_name Anchor Title + # ... + # + # and then we'll construct the actual sidebar using that list. + grep '\(.*\)<\/h[23]>/\1\t\2\t\3/' \ + > "$sb_output_file.side.list.tmp"; + in_block=0; + while read line; do + # First, extract the pieces of each line. + line_type=`echo "$line" | awk -F'\t' '{ print $1 }'`; + anchor_name=`echo "$line" | awk -F'\t' '{ print $2 }'`; + anchor_title=`echo "$line" | awk -F'\t' '{ print $3 }'`; + + # For an h2, we have to print a summary block. + # Note that this assumes that *all* h2s have h3 children. If that's not + # true, some extra processing will be needed. + if [ "$line_type" = "h2" ]; + then + if [ "$in_block" = "1" ]; + then + # We have to close the previous block. + echo "

  • " >> "$sb_output_file.side.tmp"; + fi + + # Create the new details block. + echo "
  • " >> "$sb_output_file.side.tmp"; + echo "" >> "$sb_output_file.side.tmp"; + echo "$anchor_title" >> "$sb_output_file.side.tmp"; + echo "" >> "$sb_output_file.side.tmp"; + echo "" >> "$sb_output_file.side.tmp"; + echo "
  • " >> "$sb_output_file.side.tmp"; + fi + + rm -f "$sb_output_file.side.list.tmp"; + else + # On other pages, the page title is encoded as an h1. + page_title=`grep '

    \(.*\)<\/h1>/\1/'`; + + grep '

    \(.*\)<\/h2>/
  • \2<\/a><\/li>/' \ + > "$sb_output_file.side.tmp"; + fi + lines=`cat "$sb_output_file.side.tmp" | wc -l`; + + echo "" >> "$sb_output_file"; + echo "" >> "$sb_output_file"; + + rm -f "$sb_output_file.side.tmp"; +} + +# Save any existing template. +if [ -f "$template_html_header" ]; +then + mv "$template_html_header" template.html.header.tmp; +fi + +if [ -f "$template_html_footer" ]; +then + mv "$template_html_footer" template.html.footer.tmp; +fi + +rm -rf "$output_dir"; +mkdir -p "$output_dir"; +cp -v doc/css/* "$output_dir"; +mkdir -p "$output_dir/img/"; +cp -v doc/img/* "$output_dir/img/"; +mkdir -p "$output_dir/tutorials/res/"; +cp -v doc/tutorials/res/* "$output_dir/tutorials/res/"; + +# Create the template files we will use, if they don't already exist. +if [ -f template.html.header.tmp ]; +then + mv template.html.header.tmp "$template_html_header"; +else + create_template_header "$template_html_header"; + del_header=1; +fi + +if [ -f template.html.footer.tmp ]; +then + mv template.html.footer.tmp "$template_html_footer"; +else + create_template_footer "$template_html_footer"; + del_footer=1; +fi + +cp doc/sidebar.html "$template_html_sidebar"; + +# Process all the .md files. +for f in README.md HISTORY.md `find ./doc/ -iname '*.md'`; +do + # Skip the JOSS paper... + if [[ $f == *"joss_paper"* ]]; then + continue; + fi + + echo "Processing $f..."; + run_kramdown $f; + + # This converts, e.g., ./doc/user/index.md -> doc/html/user/index.html. + tmp=${f#./doc/}; # Strip leading ./doc/. + of="$output_dir/${tmp%.md}.html"; + + tidy -qe "$of" || exit 1; +done + +# Now take a second pass to check all the links. +find "$output_dir" -iname '*.html' -print0 | while read -d $'\0' f +do + echo "Checking links in $f..."; + + # To run checklink we have to strip out some perl stderr warnings... + checklink -qs \ + --follow-file-links \ + --suppress-broken 405 \ + --suppress-broken 301 \ + -X "https://eigen.tuxfamily.org/index.php\?title=Main_Page" \ + -X "https://mlpack.slack.com/" "$f" 2>&1 | + grep -v 'Use of uninitialized value' > checklink_out; + if [ -s checklink_out ]; + then + cat checklink_out; + exit 1; + fi + rm -f checklink_out; +done + +# Remove temporary files. +if [ "a$del_header" == "a1" ]; +then + rm -f "$template_html_header"; +fi + +if [ "a$del_footer" == "a1" ]; +then + rm -f "$template_html_footer"; +fi diff --git a/scripts/check-markdown-docs.sh b/scripts/check-markdown-docs.sh new file mode 100755 index 0000000000..11c0d2993a --- /dev/null +++ b/scripts/check-markdown-docs.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# +# This script checks to ensure that generated Markdown documentation is the same +# as what's committed to the repository. After configuring mlpack with +# -DBUILD_MARKDOWN_BINDINGS=ON, use this simple script to detect changes in the +# documentation that should be committed. +# +# The only argument of the script is to the build directory. +if [ "$#" -ne 1 ]; +then + echo "Usage: $0 build/"; + echo " (replace build/ with your build directory, where you already" + echo " ran 'make markdown')"; + exit 1; +fi + +build_dir="$1"; + +# Check that Markdown documentation has been built. +if [[ ! -d "$build_dir/doc/" ]]; +then + echo "$build_dir/doc/ does not exist!"; + echo "Did you run 'make markdown' in your build directory ($build_dir)?"; + exit 1; +fi + +# Now check every file in the main repository bindings. +for f in doc/user/bindings/*; +do + echo "Checking $f..."; + f_base=`basename $f`; + + if [[ ! -f "$build_dir/doc/$f_base" ]]; + then + echo "$build_dir/doc/$f_base does not exist!"; + echo "Did you run 'make markdown' in your build directory ($build_dir)?"; + echo "Or does the file need to be removed from the repository?"; + exit 1; + fi + + diff -Nau $f "$build_dir/doc/$f_base"; + + if [ "$?" -ne 0 ]; + then + echo ""; + echo ""; + echo "Files $f and $build_dir/doc/$f_base differ! (See above.)"; + echo ""; + echo "If the sidebar differs, be sure to check if updates are needed in "; + echo "quickstart/*.sidebar.html!"; + exit 1; + fi +done diff --git a/scripts/release-mlpack.sh b/scripts/release-mlpack.sh new file mode 100755 index 0000000000..7999bc0063 --- /dev/null +++ b/scripts/release-mlpack.sh @@ -0,0 +1,216 @@ +#!/usr/bin/env bash +# +# Release a new version of mlpack. +# +# Usage: release-mlpack.sh github_username X Y Z +# +# where X is the major version, Y is the minor version, and Z is the patch +# version. Run this from the root of the repository. +# +# Make sure HISTORY.md is updated first! +set +e + +if [ "$#" -ne "4" ]; +then + echo "Usage: mlpack-release.sh "; + exit 1; +fi + +# First, check for any unlicensed files. +output=$( + for i in $(find src/ -iname '*.[hc]pp'); + do + echo -n $i": "; + cat $i | grep 'mlpack is free software;' | wc -l; + done |\ + grep -v ': 1' |\ + grep -v 'arma_extend' |\ + grep -v 'core/cereal' |\ + grep -v 'std_backport' |\ + grep -v 'arma_config.hpp' |\ + grep -v 'gitversion.hpp' |\ + grep -v 'CLI11.hpp' |\ + grep -v 'bindings/R/mlpack/src/boost/serialization' |\ + grep -v 'tests/catch.hpp'); +lines=`echo $output | grep -v '^[ ]*$' | wc -l`; + +if [ "0$lines" -gt "0" ]; +then + echo "Unlicensed files found! Aborting release."; + echo "$output"; + exit 1; +fi + +# Now, check that there are no local changes. +lines=`git diff | wc -l | sed -e 's/^\s*//g'`; +if [ "$lines" != "0" ]; then + echo "git diff returned a nonzero result!"; + echo ""; + git diff; + exit 1; +fi + +# Next, make sure the origin is right. +dest_remote_name=`git remote -v |\ + grep "mlpack/mlpack (fetch)" |\ + head -1 |\ + awk -F' ' '{ print $1 }'`; + +if [ "a$dest_remote_name" == "a" ]; then + echo "No git remote found for https://github.com/mlpack/mlpack!"; + echo "Make sure that you've got the ensmallen repository as a remote, and" \ + "that the master branch from that remote is checked out."; + echo "You can do this with a fresh repository via \`git clone" \ + "https://github.com/mlpack/mlpack\`."; + exit 1; +fi + +# Also check that we're on the master branch, from the correct origin. +current_branch=`git branch --no-color | grep '^\* ' | awk -F' ' '{ print $2 }'`; +current_origin=`git rev-parse --abbrev-ref --symbolic-full-name @{u} |\ + awk -F'/' '{ print $1 }'`; +if [ "a$current_branch" != "amaster" ]; then + echo "Current branch is $current_branch."; + echo "This script has to be run from the master branch."; + exit 1; +elif [ "a$current_origin" != "a$dest_remote_name" ]; then + echo "Current branch does not track from remote mlpack repository!"; + echo "Instead, it tracks from $current_origin/master."; + echo "Make sure to check out a branch that tracks $dest_remote_name/master."; + exit 1; +fi + +# Make sure `hub` is installed. +hub_output="`which hub`" || true; +if [ "a$hub_output" == "a" ]; then + echo "The Hub command-line tool must be installed for this script to run" \ + "successfully."; + echo "See https://hub.github.com for more details and installation" \ + "instructions."; + echo ""; + echo "(apt-get install hub on Debian and Ubuntu)"; + echo "(brew install hub via Homebrew)"; + exit 1; +fi + +# Check git remotes: we need to make sure we have a fork to push to. +github_user=$1; +remote_name=`git remote -v |\ + grep "$github_user/mlpack (push)" |\ + head -1 |\ + awk -F' ' '{ print $1 }'`; +if [ "a$remote_name" == "a" ]; then + echo "No git remote found for $github_user/mlpack!"; + echo "Adding remote '$github_user'."; + git remote add $github_user https://github.com/$github_user/mlpack; + remote_name="$github_user"; +fi +git fetch $github_user; + +# Make sure everything is up to date. +git pull; + +# Make updates to files that will be needed for the release. +MAJOR="$2"; +MINOR="$3"; +PATCH="$4"; + +# Update version. +sed --in-place 's/MLPACK_VERSION_MAJOR [0-9]*$/MLPACK_VERSION_MAJOR '$MAJOR'/' \ + src/mlpack/core/util/version.hpp; +sed --in-place 's/MLPACK_VERSION_MINOR [0-9]*$/MLPACK_VERSION_MINOR '$MINOR'/' \ + src/mlpack/core/util/version.hpp; +sed --in-place 's/MLPACK_VERSION_PATCH [0-9]*$/MLPACK_VERSION_PATCH '$PATCH'/' \ + src/mlpack/core/util/version.hpp; + +sed --in-place 's/mlpack-[0-9]\.[0-9]\.[0-9]/mlpack-'$MAJOR'.'$MINOR'.'$PATCH'/g' \ + doc/user/sample_ml_app.md; +sed --in-place 's/mlpack-[0-9]\.[0-9]\.[0-9]/mlpack-'$MAJOR'.'$MINOR'.'$PATCH'/g' \ + doc/examples/sample-ml-app/README.txt; +sed --in-place 's/mlpack-[0-9]\.[0-9]\.[0-9]/mlpack-'$MAJOR'.'$MINOR'.'$PATCH'/g' \ + doc/examples/sample-ml-app/sample-ml-app/sample-ml-app.vcxproj; + +sed --in-place 's/mlpack-[0-9]\.[0-9]\.[0-9]/mlpack-'$MAJOR'.'$MINOR'.'$PATCH'/g' \ + README.md; +sed --in-place 's/([0-9]\.[0-9]\.[0-9])/('$MAJOR'.'$MINOR'.'$PATCH')/g' \ + README.md; +sed --in-place 's/mlpack [0-9]\.[0-9]\.[0-9]/mlpack '$MAJOR'.'$MINOR'.'$PATCH'/g' \ + README.md; + +sed --in-place 's/### mlpack ?[.]?[.]?/### mlpack '$MAJOR'.'$MINOR'.'$PATCH'/g' HISTORY.md; +year=`date +%Y`; +month=`date +%m`; +day=`date +%d`; +sed --in-place 's/###### ????-??-??/###### '$year'-'$month'-'$day'/g' \ + HISTORY.md; + +# Get the latest release of ensmallen. +git clone https://github.com/mlpack/ensmallen /tmp/ensmallen; +cd /tmp/ensmallen; +ens_ver=`git describe --tags $(git rev-list --tags --max-count=1)`; +echo "Latest version of ensmallen: $ens_ver" +cd -; +sed --in-place "s/ensmallen-latest.tar.gz/ensmallen-$ens_ver.tar.gz/" CMakeLists.txt; +rm -rf /tmp/ensmallen; + +# Make these changes on a release branch. +git checkout -b release-$MAJOR.$MINOR.$PATCH; + +git add src/mlpack/core/util/version.hpp \ + doc/user/sample_ml_app.md \ + doc/examples/sample-ml-app/README.txt \ + doc/examples/sample-ml-app/sample-ml-app/sample-ml-app.vcxproj \ + CMakeLists.txt \ + README.md \ + HISTORY.md; + +git commit -m "Update and release version $MAJOR.$MINOR.$PATCH."; + +changelog_str=`cat HISTORY.md |\ + awk '/^### /{f=0} /^### mlpack '"$MAJOR"'.'"$MINOR"'.'"$PATCH"'/{f=1} f{print}' |\ + grep -v '^#' |\ + tr '\n' '!' |\ + sed -e 's/! [ ]*/ /g' |\ + tr '!' '\n'`; +echo "Changelog string:" +echo "$changelog_str" + +# Update version again and add a new block for HISTORY.md. +sed --in-place 's/MLPACK_VERSION_PATCH [0-9]*$/MLPACK_VERSION_PATCH '$(($PATCH + 1))'/' \ + src/mlpack/core/util/version.hpp; +sed --in-place 's/ensmallen-'$ens_ver'.tar.gz/ensmallen-latest.tar.gz/' CMakeLists.txt; + +echo "### mlpack ?.?.?" > HISTORY.md.new; +echo "###### ????-??-??" >> HISTORY.md.new; +echo "" >> HISTORY.md.new; +cat HISTORY.md >> HISTORY.md.new; +mv HISTORY.md.new HISTORY.md; + +git add HISTORY.md; +git add src/mlpack/core/util/version.hpp CMakeLists.txt; + +git commit -m "Add new block for next release to HISTORY.md."; + +# Push to new branch. +git push --set-upstream $github_user release-$MAJOR.$MINOR.$PATCH; + +# Next, we have to actually open the PR for the release. +hub pull-request \ + -b mlpack:master \ + -h $github_user:release-$MAJOR.$MINOR.$PATCH \ + -m "Release version $MAJOR.$MINOR.$PATCH" \ + -m "This automatically-generated pull request adds the commits necessary to +make the $MAJOR.$MINOR.$PATCH release." \ + -m "Once the PR is merged, mlpack-bot will tag the release as HEAD~1 (so +that it doesn't include the new HISTORY block) and publish it." \ + -m "Or, well, hopefully that will happen someday." \ + -m "When you merge this PR, be sure to merge it using a *rebase*." \ + -m "### Changelog" \ + -m "$changelog_str" \ + -l "t: release" + +echo ""; +echo "Switching back to 'master' branch."; +echo "If you want to access the release branch again, use \`git checkout " \ + "release-$MAJOR.$MINOR.$PATCH\`."; +echo 0; diff --git a/scripts/test-docs.sh b/scripts/test-docs.sh new file mode 100755 index 0000000000..a787f04362 --- /dev/null +++ b/scripts/test-docs.sh @@ -0,0 +1,407 @@ +#!/usr/bin/env bash +# +# Extract C++ code blocks from either an individual Markdown file or a directory +# full of Markdown files. This does roughly what you would expect it to, but +# there is a little bit of magic: +# +# * All ```c++ code blocks are extracted into their own .cpp files. +# +# * mlpack.hpp is included in each file, and each code block is placed in an +# `int main() { }` block. +# +# * If Eigen or xtensor is detected, correct includes are added. +# +# * All data referenced in http URLs is downloaded. +# +# * If a code block is just a class declaration, it will be added to the *next* +# code block file. +# +# Once all code blocks are compiled, they are run just to make sure they run +# correctly. If a single file was passed, all inputs and outputs are printed; +# if an entire directory is given, program output is only printed on error. + +if [[ $# -ne 1 ]]; +then + echo "Usages: " >&2; + echo " - $0 input_file.md" >&2; + echo " - $0 markdown_directory/" >&2; + exit 2; +fi + +if [ -z "$CXX" ]; +then + echo "You must set \$CXX to the compiler you want to use!" >&2; + exit 1; +fi + +if [ -z "$CXXFLAGS" ]; +then + echo "Warning: \$CXXFLAGS is unset. If ensmallen, Armadillo, STB, or cereal "; + echo " are not in standard locations, builds will fail! Be sure to use "; + echo " absolute paths, not relative paths."; +fi + +if [ -z "$LDFLAGS" ]; +then + echo "Warning: \$LDFLAGS is unset. If libarmadillo.so is not in a standard "; + echo " location, builds will fail!"; +fi + +# First determine what files we are looking at. +if [ -d $1 ]; +then + files=`find $1 -iname '*.md'`; + mode="directory"; +else + files=$1; + mode="file"; +fi + +# Extract the C++ code blocks from a particular file, creating +# $output_prefix1.cpp, $output_prefix2.cpp, and so on and so forth. +# +# The code in those snippets will be placed into an int main() { } block, and +# mlpack.hpp will be included. +extract_code_blocks() +{ + input_file=$1; + output_prefix=$2; + + # Extract into temporary files. + sed -n '/^```c++/,/^```/ p' < $input_file > $input_file.tmp; + output_file_id=0; + output_file_display="00"; # Hopefully no file has more than 100 examples... + + # Track whether or not the last line was a fence, since we get them two at a + # time. We initially set this to 1, because the first fence does not have a + # preceding fence close above it. + last_line_fence=1; + + # Track whether or not the entire last file corresponded to a class + # declaration. + class_decl=0; + + while IFS= read -r line; + do + if [[ $last_line_fence == 1 ]]; + then + # Skip this line---it will be a fence opening. + last_line_fence=0; + + # Create main() function to wrap the code in. + echo "#include " > $output_prefix$output_file_display.cpp; + echo "" >> $output_prefix$output_file_display.cpp; + + # If we have a class declaration from the previous file, insert it. + if [[ $class_decl == 1 ]]; + then + class_decl=0; + last_output_file_id=$(($output_file_id - 1)); + last_output_file_display=$(printf "%02d" $last_output_file_id); + + cat $output_prefix$last_output_file_display.cpp | awk ' + BEGIN { p=0 } + /int main()/ { p=1 } + /^{/ { if(p == 1) { p=2; o=1 } } + /^}/ { p=0; } + // { if (p == 2 && o == 0) { print substr($0, 3) } o=0 }' >> $output_prefix$output_file_display.cpp; + echo "" >> $output_prefix$output_file_display.cpp; + rm -f $output_prefix$last_output_file_display.cpp; + fi + + echo "int main()" >> $output_prefix$output_file_display.cpp; + echo "{" >> $output_prefix$output_file_display.cpp; + continue; + fi + + if [[ $line == '```'* ]]; + then + last_line_fence=1; + + # Close main() function. + echo "}" >> $output_prefix$output_file_display.cpp; + + # Check after the fact: was this file only a class declaration? If so, we + # want to put it instead into the next file. + has_class1=`grep '^ class' $output_prefix$output_file_display.cpp | wc -l`; + has_class2=`grep '^ };' $output_prefix$output_file_display.cpp | wc -l`; + if [[ "$has_class1" != "0" && "$has_class2" != "0" ]]; + then + class_decl=1; + fi; + + # Detect if we need any to add any special headers. We have to do this + # when we finish with the file... + if [[ `grep 'Eigen::' $output_prefix$output_file_display.cpp | wc -l` -gt 0 ]]; + then + sed -i '1s/^/#include \n/' $output_prefix$output_file_display.cpp; + fi + + if [[ `grep 'xt::' $output_prefix$output_file_display.cpp | wc -l` -gt 0 ]]; + then + sed -i '1s/^/#include \n/' $output_prefix$output_file_display.cpp; + sed -i '1s/^/#include \n/' $output_prefix$output_file_display.cpp; + fi + + output_file_id=$(($output_file_id + 1)); + output_file_display=$(printf "%02d" $output_file_id); + + continue; + fi + + # Include indentation (two spaces). + echo " $line" >> $output_prefix$output_file_display.cpp; + done < $input_file.tmp; + + # The last file is always invalid---we opened it without knowing whether + # anything would be in it. + rm -f $output_prefix$output_file_display.cpp; + + # Check the "true" last file: if it's only class declarations, no need to + # compile it. + output_file_id=$(($output_file_id - 1)); + output_file_display=$(printf "%02d" $output_file_id); + if [ -f $output_prefix$output_file_display.cpp ]; + then + cat $output_prefix$output_file_display.cpp | awk ' + BEGIN { p=0 } + /int main()/ { p=1 } + /^{/ { if(p == 1) { p=2; o=1 } } + /^}/ { p=0 } + // { if (p == 2 && o == 0) { print substr($0, 3) } o=0 }' >> $output_prefix$output_file_display.cpp.tmp; + has_class1=`grep '^class' $output_prefix$output_file_display.cpp.tmp | wc -l`; + has_class2=`grep '^};' $output_prefix$output_file_display.cpp.tmp | wc -l`; + if [[ "$has_class1" != "0" && "$has_class2" != "0" ]]; + then + # The file's main() function is just a class declaration. Nuke it. + rm -f $output_prefix$output_file_display.cpp; + fi + rm -f $output_prefix$output_file_display.cpp.tmp; + fi + + rm -f $input_file.tmp; +} + +compile_code_blocks() +{ + input_dir=$1; + + # If there are no files to compile, leave early. + if ! compgen -G $input_dir/*.cpp >/dev/null; + then + return; + fi + + for f in $input_dir/*.cpp; + do + echo " Compiling $f..."; + of=${f%.cpp}; + + if ! $CXX -std=c++17 -Isrc/ $CXXFLAGS -o $of $f $LDFLAGS -larmadillo 2>$of.tmp; + then + echo "Compilation of the following program failed:"; + echo ""; + cat $f; + echo ""; + echo "First ten lines of error output:"; + head $of.tmp; + echo ""; + echo "For full error output run either:"; + echo " - less $of.tmp"; + echo " - $CXX -std=c++17 -Isrc/ $CXXFLAGS -o $of $f $LDFLAGS -larmadillo"; + echo ""; + echo "Did you set \$CXX, \$CXXFLAGS, and \$LDFLAGS correctly?" + exit 1; + fi + done +} + +download_http_artifacts() +{ + input_dir=$1; + output_dir=$2; + + # Get a list of all HTTP resources. + artifacts=`grep 'http[s]*://' $input_dir/*.cpp |\ + sed 's/^.*\(http[^ ]*\).*$/\1/' |\ + sort |\ + uniq |\ + grep 'csv\|arff\|bin\|png' |\ + sed 's/\.$//'`; + cd $output_dir; + for a in $artifacts; + do + out_a=`basename $a`; + if [ ! -f $out_a ]; + then + echo " Downloading $a..."; + if ! curl -s -O $a; + then + echo "Error downloading $a!"; + exit 1; + fi + fi + done + cd - >/dev/null; + + # Special case: if we are looking at core.md, this has two special files we + # need to create that is used in the example. + f=`basename $input_dir`; + if [[ "$f" == "core" || "$f" == "matrices" ]]; + then + cd $output_dir; + echo " Creating data.csv..."; + cat > data.csv << EOF +3,3,3,3,0 +3,4,4,3,0 +3,4,4,3,0 +3,3,4,3,0 +3,6,4,3,0 +2,4,4,3,0 +2,4,4,1,0 +3,3,3,2,0 +3,4,4,2,0 +3,4,4,2,0 +3,3,4,2,0 +3,6,4,2,0 +2,4,4,2,0 +EOF + + echo " Creating mixed_string_data.csv..."; + cat > mixed_string_data.csv << EOF +3,"hello",3,"f",0 +3,"goodbye",4,"f",0 +3,"goodbye",4,"e",0 +3,"hello",4,"d",0 +3,"hello",4,"d",0 +2,"hello",4,"d",0 +2,"hello",4,"d",0 +3,"goodbye",3,"f",0 +3,"goodbye",4,"f",0 +3,"hello",4,"f",0 +3,"hello",4,"c",0 +3,"hello",4,"f",0 +2,"hello",4,"c",0 +EOF + cd - >/dev/null; + fi +} + +run_code_blocks() +{ + input_dir=$1; + + for f in $input_dir/*.cpp; + do + f_exec=${f%.cpp}; + if [[ "$mode" == "directory" ]]; + then + echo " Running $f_exec..."; + if ! ./$f_exec 2>&1 >/dev/null; + then + echo " Error running $f_exec!"; + exit 1; + fi + else + echo " --------------------------------------------------------------------- "; + echo " Contents of $f:"; + echo ""; + cat $f; + echo ""; + echo " --------------------------------------------------------------------- "; + echo " Output of $f_exec:"; + echo ""; + + if ! ./$f_exec; + then + echo ""; + echo "Error running $f_exec! See output above."; + exit 1; + fi + echo ""; + echo " --------------------------------------------------------------------- "; + fi + done +} + +# Main loop: process the files we were asked to process. +mkdir -p doc/build/; +for f in $files; +do + if [[ "$mode" == "directory" ]]; + then + declare -a files_to_skip=( + # These files have small incomplete snippets that can't compile into + # standalone programs. + "sample_ml_app.md" + "hpt.md" + "cv.md" + "timer.md" + "bindings.md" + "elemtype.md" + "iodoc.md" + "kernels.md" + "metrics.md" + "trees.md" + # The tutorials are old and are likely to be replaced, so let's not test + # them. + "amf.md" + "ann.md" + "approx_kfn.md" + "cf.md" + "datasetmapper.md" + "det.md" + "emst.md" + "fastmks.md" + "image.md" + "kmeans.md" + "linear_regression.md" + "neighbor_search.md" + "range_search.md" + "reinforcement_learning.md" + "asynchronous_learning.md" + "ddpg.md" + "q_learning.md" + "sac.md" + "td3.md" + # Skip quickstarts, although we should eventually test them. + "cpp.md" + ); + + skip=0; + for skip_f in "${files_to_skip[@]}"; + do + base_f=`basename $f`; + if [ "$base_f" = "$skip_f" ]; + then + skip=1; + break; + fi + done + + if [[ $skip -eq 1 ]]; + then + continue; + fi + fi + + echo "Building documentation for $f..."; + + build_dir_tmp=${f#doc/}; + build_dir=${build_dir_tmp%.md}; + base_file=`basename $f .md`; + mkdir -p doc/build/$build_dir/; + + extract_code_blocks $f doc/build/$build_dir/$base_file; + # If there are no C++ files, don't do anything else.. + if ! compgen -G doc/build/$build_dir/*.cpp >/dev/null; + then + continue; + fi + + compile_code_blocks doc/build/$build_dir; + download_http_artifacts doc/build/$build_dir doc/build/; + cd doc/build/; + run_code_blocks $build_dir; + cd ../../; +done diff --git a/scripts/update-website-after-release.sh b/scripts/update-website-after-release.sh new file mode 100755 index 0000000000..e80ad8694f --- /dev/null +++ b/scripts/update-website-after-release.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# +# This script is used to update the website after an mlpack release is made. +# Push access to the mlpack.org website repository is needed. Generally, this +# script will be run by mlpack-bot, so it never needs to be run by hand. +# +# Usage: update-website-after-release.sh + +MAJOR=$1; +MINOR=$2; +PATCH=$3; + +# Make sure that the mlpack repository exists. +dest_remote_name=`git remote -v |\ + grep "mlpack/mlpack (fetch)" |\ + head -1 |\ + awk -F' ' '{ print $1 }'`; + +if [ "a$dest_remote_name" == "a" ]; then + echo "No git remote found for mlpack/mlpack!"; + echo "Make sure that you've got the mlpack repository as a remote, and" \ + "that the master branch from that remote is checked out."; + echo "You can do this with a fresh repository via \`git clone" \ + "https://github.com/mlpack/mlpack\`."; + exit 1; +fi + +# Update the checked out repository, so that we can get the tags. +git fetch $dest_remote_name; + +# Check out a copy of the ensmallen.org repository. +git clone git@github.com:mlpack/mlpack.org /tmp/mlpack.org/; + +# Create the release file. +git archive --prefix=mlpack-$MAJOR.$MINOR.$PATCH/ $MAJOR.$MINOR.$PATCH |\ + gzip > /tmp/mlpack.org/files/mlpack-$MAJOR.$MINOR.$PATCH.tar.gz; + +# Now update the website. +wd=`pwd`; +cd /tmp/mlpack.org/; + +# These may be specific to the old website. +sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' index.md; +sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' docs.md; +sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' getstarted.md; +sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' community.md; +git add index.md docs.md getstarted.md community.md; + +# These may be specific to the new website. +sed --in-place 's/mlpack-[0-9]\.[0-9]\.[0-9]/mlpack-'$MAJOR'.'$MINOR'.'$PATCH'/g' html/index.html; +sed --in-place 's/Version [0-9]\.[0-9]\.[0-9]/Version '$MAJOR'.'$MINOR'.'$PATCH'/g' html/index.html; +sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' html/getstarted.html; +sed --in-place 's/[0-9]\.[0-9]\.[0-9]/'$MAJOR'.'$MINOR'.'$PATCH'/g' html/config/install.md; +git add html/index.html html/getstarted.html html/config/install.md; + +git commit -m "Update links to latest stable version."; + +git add files/mlpack-$MAJOR.$MINOR.$PATCH.tar.gz; +git commit -m "Release version $MAJOR.$MINOR.$PATCH."; + +# Finally, push, and we're done. +git push origin; +cd $wd; + +rm -rf /tmp/mlpack.org; From 7a927b947ebe87602539c573b33435012cfaed6c Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 26 Jun 2024 19:45:26 +0200 Subject: [PATCH 075/212] remove unwanted files --- scripts/release-mlpack.sh | 26 ++++++++++++++------------ src/mlpack/bindings/R/mlpack/cleanup | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) mode change 100755 => 100644 scripts/release-mlpack.sh diff --git a/scripts/release-mlpack.sh b/scripts/release-mlpack.sh old mode 100755 new mode 100644 index 7999bc0063..5bddc02664 --- a/scripts/release-mlpack.sh +++ b/scripts/release-mlpack.sh @@ -137,11 +137,11 @@ sed --in-place 's/([0-9]\.[0-9]\.[0-9])/('$MAJOR'.'$MINOR'.'$PATCH')/g' \ sed --in-place 's/mlpack [0-9]\.[0-9]\.[0-9]/mlpack '$MAJOR'.'$MINOR'.'$PATCH'/g' \ README.md; -sed --in-place 's/### mlpack ?[.]?[.]?/### mlpack '$MAJOR'.'$MINOR'.'$PATCH'/g' HISTORY.md; +sed --in-place 's/## mlpack ?[.]?[.]?/## mlpack '$MAJOR'.'$MINOR'.'$PATCH'/g' HISTORY.md; year=`date +%Y`; month=`date +%m`; day=`date +%d`; -sed --in-place 's/###### ????-??-??/###### '$year'-'$month'-'$day'/g' \ +sed --in-place 's/_????-??-??_/_'$year'-'$month'-'$day'_/g' \ HISTORY.md; # Get the latest release of ensmallen. @@ -167,8 +167,9 @@ git add src/mlpack/core/util/version.hpp \ git commit -m "Update and release version $MAJOR.$MINOR.$PATCH."; changelog_str=`cat HISTORY.md |\ - awk '/^### /{f=0} /^### mlpack '"$MAJOR"'.'"$MINOR"'.'"$PATCH"'/{f=1} f{print}' |\ + awk '/^## /{f=0} /^## mlpack '"$MAJOR"'.'"$MINOR"'.'"$PATCH"'/{f=1} f{print}' |\ grep -v '^#' |\ + grep -v '^_' |\ tr '\n' '!' |\ sed -e 's/! [ ]*/ /g' |\ tr '!' '\n'`; @@ -180,10 +181,13 @@ sed --in-place 's/MLPACK_VERSION_PATCH [0-9]*$/MLPACK_VERSION_PATCH '$(($PATCH + src/mlpack/core/util/version.hpp; sed --in-place 's/ensmallen-'$ens_ver'.tar.gz/ensmallen-latest.tar.gz/' CMakeLists.txt; -echo "### mlpack ?.?.?" > HISTORY.md.new; -echo "###### ????-??-??" >> HISTORY.md.new; +echo "# mlpack changelog" > HISTORY.md.new; echo "" >> HISTORY.md.new; -cat HISTORY.md >> HISTORY.md.new; +echo "## mlpack ?.?.?" >> HISTORY.md.new; +echo "" >> HISTORY.md.new; +echo "_????-??-??_" >> HISTORY.md.new; +echo "" >> HISTORY.md.new; +cat HISTORY.md | grep -v '^# mlpack changelog' >> HISTORY.md.new; mv HISTORY.md.new HISTORY.md; git add HISTORY.md; @@ -199,11 +203,8 @@ hub pull-request \ -b mlpack:master \ -h $github_user:release-$MAJOR.$MINOR.$PATCH \ -m "Release version $MAJOR.$MINOR.$PATCH" \ - -m "This automatically-generated pull request adds the commits necessary to -make the $MAJOR.$MINOR.$PATCH release." \ - -m "Once the PR is merged, mlpack-bot will tag the release as HEAD~1 (so -that it doesn't include the new HISTORY block) and publish it." \ - -m "Or, well, hopefully that will happen someday." \ + -m "This automatically-generated pull request adds the commits necessary to make the $MAJOR.$MINOR.$PATCH release." \ + -m "Once the PR is merged, mlpack-bot will tag the release as HEAD~1 (so that it doesn't include the new HISTORY block) and publish it." \ -m "When you merge this PR, be sure to merge it using a *rebase*." \ -m "### Changelog" \ -m "$changelog_str" \ @@ -213,4 +214,5 @@ echo ""; echo "Switching back to 'master' branch."; echo "If you want to access the release branch again, use \`git checkout " \ "release-$MAJOR.$MINOR.$PATCH\`."; -echo 0; +git checkout master; +echo 0; \ No newline at end of file diff --git a/src/mlpack/bindings/R/mlpack/cleanup b/src/mlpack/bindings/R/mlpack/cleanup index 5347fead4a..f5a3721ed4 100644 --- a/src/mlpack/bindings/R/mlpack/cleanup +++ b/src/mlpack/bindings/R/mlpack/cleanup @@ -1,2 +1,2 @@ ## compilation and editing objects -rm -f src/*.o src/*.so src/*.dylib src/*~ *~ +rm -f src/*.o src/*.so src/*.dylib src/*~ *~ \ No newline at end of file From 158406dd56a323416851e82a21bb4ed27e311db6 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Thu, 27 Jun 2024 13:26:23 +0200 Subject: [PATCH 076/212] restore permissions --- scripts/release-mlpack.sh | 0 src/mlpack/bindings/R/mlpack/cleanup | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/release-mlpack.sh mode change 100644 => 100755 src/mlpack/bindings/R/mlpack/cleanup diff --git a/scripts/release-mlpack.sh b/scripts/release-mlpack.sh old mode 100644 new mode 100755 diff --git a/src/mlpack/bindings/R/mlpack/cleanup b/src/mlpack/bindings/R/mlpack/cleanup old mode 100644 new mode 100755 From 7f088afcabfb17a02591893f0416d106d1c32364 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Thu, 27 Jun 2024 13:29:26 +0200 Subject: [PATCH 077/212] restore --- scripts/release-mlpack.sh | 2 +- src/mlpack/bindings/R/mlpack/cleanup | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/release-mlpack.sh b/scripts/release-mlpack.sh index 5bddc02664..2092df3bcf 100755 --- a/scripts/release-mlpack.sh +++ b/scripts/release-mlpack.sh @@ -215,4 +215,4 @@ echo "Switching back to 'master' branch."; echo "If you want to access the release branch again, use \`git checkout " \ "release-$MAJOR.$MINOR.$PATCH\`."; git checkout master; -echo 0; \ No newline at end of file +echo 0; diff --git a/src/mlpack/bindings/R/mlpack/cleanup b/src/mlpack/bindings/R/mlpack/cleanup index f5a3721ed4..5347fead4a 100755 --- a/src/mlpack/bindings/R/mlpack/cleanup +++ b/src/mlpack/bindings/R/mlpack/cleanup @@ -1,2 +1,2 @@ ## compilation and editing objects -rm -f src/*.o src/*.so src/*.dylib src/*~ *~ \ No newline at end of file +rm -f src/*.o src/*.so src/*.dylib src/*~ *~ From c3d54b373a23bae99fa2c75499260bea46c6a53b Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Thu, 27 Jun 2024 14:18:42 +0200 Subject: [PATCH 078/212] fix: ForwardImplementation template --- src/mlpack/methods/ann/layer/dropout.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/ann/layer/dropout.hpp b/src/mlpack/methods/ann/layer/dropout.hpp index 5780082f1c..914e1ab144 100644 --- a/src/mlpack/methods/ann/layer/dropout.hpp +++ b/src/mlpack/methods/ann/layer/dropout.hpp @@ -83,8 +83,9 @@ class DropoutType : public Layer * @param input Input data used for evaluating the specified function. * @param output Resulting output activation. */ - template::value>* = 0> - void ForwardImpl(const MatType& input, MatType& output); + template::value, int> = 0> + void ForwardImpl(const T& input, T& output); + /** * General implementation of the forward pass of the dropout layer. @@ -92,8 +93,8 @@ class DropoutType : public Layer * @param input Input data used for evaluating the specified function. * @param output Resulting output activation. */ - template::value>* = 0> - void ForwardImpl(const MatType& input, MatType& output); + template::value, int> = 0> + void ForwardImpl(const T& input, T& output); /** * Ordinary feed backward pass of the dropout layer. From 5fe74021afe105ff3c23d16c4bc65e01ea68c099 Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:54:23 +0200 Subject: [PATCH 079/212] Update HISTORY.md --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 05062d0867..dad91ea159 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,7 +5,7 @@ _????-??-??_ * Distribute STB headers as part of R package (#3724, #3726). - + * Added OpenMP support for fast approximation (#3685). ## mlpack 4.4.0 From b82f017ceabb499eb746f2f37591bb9ea3072a2f Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:57:09 +0200 Subject: [PATCH 080/212] Update HISTORY.md --- HISTORY.md | 1 - 1 file changed, 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index dad91ea159..44d9c644af 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,7 +5,6 @@ _????-??-??_ * Distribute STB headers as part of R package (#3724, #3726). - * Added OpenMP support for fast approximation (#3685). ## mlpack 4.4.0 From 4de58d5691a087ab3a234ab3a9e027b24ef41d73 Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:59:50 +0200 Subject: [PATCH 081/212] Update HISTORY.md --- HISTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.md b/HISTORY.md index 44d9c644af..469e77b97e 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,6 +5,7 @@ _????-??-??_ * Distribute STB headers as part of R package (#3724, #3726). + * Implemented the Find and Fill algorithm into the Dropout Layer and added OpenMP support (#3684). ## mlpack 4.4.0 From 6b8f0c1990da03647f24007dca69fc546018bc14 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Thu, 27 Jun 2024 23:09:46 +0200 Subject: [PATCH 082/212] struct: simplified the forward function --- src/mlpack/methods/ann/layer/dropout.hpp | 21 ++---------- src/mlpack/methods/ann/layer/dropout_impl.hpp | 32 ------------------- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/src/mlpack/methods/ann/layer/dropout.hpp b/src/mlpack/methods/ann/layer/dropout.hpp index 914e1ab144..c5c094e992 100644 --- a/src/mlpack/methods/ann/layer/dropout.hpp +++ b/src/mlpack/methods/ann/layer/dropout.hpp @@ -69,33 +69,16 @@ class DropoutType : public Layer //! Take ownership of the given DropoutType. DropoutType& operator=(DropoutType&& other); + /** * Ordinary feed forward pass of the dropout layer. * * @param input Input data used for evaluating the specified function. * @param output Resulting output activation. */ + template void Forward(const MatType& input, MatType& output); - /** - * Implementation of the forward pass of the dropout layer. - * - * @param input Input data used for evaluating the specified function. - * @param output Resulting output activation. - */ - template::value, int> = 0> - void ForwardImpl(const T& input, T& output); - - - /** - * General implementation of the forward pass of the dropout layer. - * - * @param input Input data used for evaluating the specified function. - * @param output Resulting output activation. - */ - template::value, int> = 0> - void ForwardImpl(const T& input, T& output); - /** * Ordinary feed backward pass of the dropout layer. * diff --git a/src/mlpack/methods/ann/layer/dropout_impl.hpp b/src/mlpack/methods/ann/layer/dropout_impl.hpp index f93ac422e9..8a825119a3 100644 --- a/src/mlpack/methods/ann/layer/dropout_impl.hpp +++ b/src/mlpack/methods/ann/layer/dropout_impl.hpp @@ -76,38 +76,6 @@ DropoutType::operator=(DropoutType&& other) template void DropoutType::Forward(const MatType& input, MatType& output) -{ - // The dropout mask will not be multiplied in testing mode. - ForwardImpl(input, output); -} - - -template -template::value, int>> -void DropoutType::ForwardImpl(const T& input, T& output) -{ - if (!this->training) - { - output = input; - } - else - { - mask.randu(input.n_rows, input.n_cols); - #pragma omp parallel for collapse(2) - for (size_t i = 0; i < input.n_rows; ++i) - { - for (size_t j = 0; j < input.n_cols; ++j) - { - mask(i, j) = (mask(i, j) > this->ratio) ? 1.0 : 0.0; - } - } - output = input % mask * this->scale; - } -} - -template -template::value, int>> -void DropoutType::ForwardImpl(const T& input, T& output) { if (!this->training) { From 5027de128c1c407410455eaa5fa72afe2e393abe Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Thu, 4 Jul 2024 15:44:50 +0200 Subject: [PATCH 083/212] fix --- src/mlpack/methods/ann/layer/dropout.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mlpack/methods/ann/layer/dropout.hpp b/src/mlpack/methods/ann/layer/dropout.hpp index c5c094e992..ad18e8e452 100644 --- a/src/mlpack/methods/ann/layer/dropout.hpp +++ b/src/mlpack/methods/ann/layer/dropout.hpp @@ -69,14 +69,12 @@ class DropoutType : public Layer //! Take ownership of the given DropoutType. DropoutType& operator=(DropoutType&& other); - /** * Ordinary feed forward pass of the dropout layer. * * @param input Input data used for evaluating the specified function. * @param output Resulting output activation. */ - template void Forward(const MatType& input, MatType& output); /** From 4ba48ad3bace7647dde57aad020919f7bc183bc6 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 6 Jul 2024 11:19:44 -0400 Subject: [PATCH 084/212] Refactor BoundType to take ElemType as a second parameter. --- src/mlpack/core/tree/ballbound.hpp | 5 +- src/mlpack/core/tree/ballbound_impl.hpp | 109 ++++--- .../binary_space_tree/binary_space_tree.hpp | 40 ++- .../binary_space_tree_impl.hpp | 304 +++++++++++------- .../breadth_first_dual_tree_traverser.hpp | 8 +- ...breadth_first_dual_tree_traverser_impl.hpp | 24 +- .../binary_space_tree/dual_tree_traverser.hpp | 8 +- .../dual_tree_traverser_impl.hpp | 16 +- .../single_tree_traverser.hpp | 8 +- .../single_tree_traverser_impl.hpp | 16 +- .../core/tree/binary_space_tree/traits.hpp | 20 +- .../tree/space_split/projection_vector.hpp | 13 +- src/mlpack/tests/dbscan_test.cpp | 14 +- src/mlpack/tests/knn_test.cpp | 18 +- src/mlpack/tests/serialization_test.cpp | 4 +- 15 files changed, 340 insertions(+), 267 deletions(-) diff --git a/src/mlpack/core/tree/ballbound.hpp b/src/mlpack/core/tree/ballbound.hpp index 0f1f8f9636..9a7e5f1977 100644 --- a/src/mlpack/core/tree/ballbound.hpp +++ b/src/mlpack/core/tree/ballbound.hpp @@ -27,12 +27,11 @@ namespace mlpack { * @tparam VecType Type of vector (arma::vec or arma::sp_vec or similar). */ template, - typename VecType = arma::vec> + typename ElemType = double, + typename VecType = arma::Col> class BallBound { public: - //! The underlying data type. - typedef typename VecType::elem_type ElemType; //! A public version of the vector type. typedef VecType Vec; diff --git a/src/mlpack/core/tree/ballbound_impl.hpp b/src/mlpack/core/tree/ballbound_impl.hpp index 123f0200cb..8793c3077c 100644 --- a/src/mlpack/core/tree/ballbound_impl.hpp +++ b/src/mlpack/core/tree/ballbound_impl.hpp @@ -20,8 +20,8 @@ namespace mlpack { //! Empty Constructor. -template -BallBound::BallBound() : +template +BallBound::BallBound() : radius(std::numeric_limits::lowest()), distance(new DistanceType()), ownsDistance(true) @@ -32,8 +32,8 @@ BallBound::BallBound() : * * @param dimension Dimensionality of ball bound. */ -template -BallBound::BallBound(const size_t dimension) : +template +BallBound::BallBound(const size_t dimension) : radius(std::numeric_limits::lowest()), center(dimension), distance(new DistanceType()), @@ -46,9 +46,9 @@ BallBound::BallBound(const size_t dimension) : * @param radius Radius of ball bound. * @param center Center of ball bound. */ -template -BallBound::BallBound(const ElemType radius, - const VecType& center) : +template +BallBound::BallBound(const ElemType radius, + const VecType& center) : radius(radius), center(center), distance(new DistanceType()), @@ -56,8 +56,8 @@ BallBound::BallBound(const ElemType radius, { /* Nothing to do. */ } //! Copy Constructor. To prevent memory leaks. -template -BallBound::BallBound(const BallBound& other) : +template +BallBound::BallBound(const BallBound& other) : radius(other.radius), center(other.center), distance(other.distance), @@ -65,8 +65,9 @@ BallBound::BallBound(const BallBound& other) : { /* Nothing to do. */ } //! For the same reason as the copy constructor: to prevent memory leaks. -template -BallBound& BallBound::operator=( +template +BallBound& +BallBound::operator=( const BallBound& other) { if (this != &other) @@ -80,8 +81,8 @@ BallBound& BallBound::operator=( } //! Move constructor. -template -BallBound::BallBound(BallBound&& other) : +template +BallBound::BallBound(BallBound&& other) : radius(other.radius), center(other.center), distance(other.distance), @@ -95,8 +96,9 @@ BallBound::BallBound(BallBound&& other) : } //! Move assignment operator. -template -BallBound& BallBound::operator=( +template +BallBound& +BallBound::operator=( BallBound&& other) { if (this != &other) @@ -115,29 +117,30 @@ BallBound& BallBound::operator=( } //! Destructor to release allocated memory. -template -BallBound::~BallBound() +template +BallBound::~BallBound() { if (ownsDistance) delete distance; } //! Get the range in a certain dimension. -template -RangeType::ElemType> -BallBound::operator[](const size_t i) const +template +RangeType +BallBound::operator[](const size_t i) const { if (radius < 0) - return Range(); + return RangeType(); else - return Range(center[i] - radius, center[i] + radius); + return RangeType(center[i] - radius, center[i] + radius); } /** * Determines if a point is within the bound. */ -template -bool BallBound::Contains(const VecType& point) const +template +bool BallBound::Contains(const VecType& point) + const { if (radius < 0) return false; @@ -148,10 +151,9 @@ bool BallBound::Contains(const VecType& point) const /** * Calculates minimum bound-to-point squared distance. */ -template +template template -typename BallBound::ElemType -BallBound::MinDistance( +ElemType BallBound::MinDistance( const OtherVecType& point, typename std::enable_if_t::value>* /* junk */) const { @@ -164,10 +166,9 @@ BallBound::MinDistance( /** * Calculates minimum bound-to-bound squared distance. */ -template -typename BallBound::ElemType -BallBound::MinDistance(const BallBound& other) - const +template +ElemType BallBound::MinDistance( + const BallBound& other) const { if (radius < 0) return std::numeric_limits::max(); @@ -182,10 +183,9 @@ BallBound::MinDistance(const BallBound& other) /** * Computes maximum distance. */ -template +template template -typename BallBound::ElemType -BallBound::MaxDistance( +ElemType BallBound::MaxDistance( const OtherVecType& point, typename std::enable_if_t::value>* /* junk */) const { @@ -198,10 +198,9 @@ BallBound::MaxDistance( /** * Computes maximum distance. */ -template -typename BallBound::ElemType -BallBound::MaxDistance(const BallBound& other) - const +template +ElemType BallBound::MaxDistance( + const BallBound& other) const { if (radius < 0) return std::numeric_limits::max(); @@ -214,36 +213,36 @@ BallBound::MaxDistance(const BallBound& other) * * Example: bound1.MinDistanceSq(other) for minimum squared distance. */ -template +template template -RangeType::ElemType> -BallBound::RangeDistance( +RangeType BallBound::RangeDistance( const OtherVecType& point, typename std::enable_if_t::value>* /* junk */) const { if (radius < 0) - return Range(std::numeric_limits::max(), - std::numeric_limits::max()); + return RangeType(std::numeric_limits::max(), + std::numeric_limits::max()); else { const ElemType dist = distance->Evaluate(center, point); - return Range(std::max(dist - radius, (ElemType) 0.0), dist + radius); + return RangeType(std::max(dist - radius, (ElemType) 0.0), + dist + radius); } } -template -RangeType::ElemType> -BallBound::RangeDistance( +template +RangeType BallBound::RangeDistance( const BallBound& other) const { if (radius < 0) - return Range(std::numeric_limits::max(), - std::numeric_limits::max()); + return RangeType(std::numeric_limits::max(), + std::numeric_limits::max()); else { const ElemType dist = distance->Evaluate(center, other.center); const ElemType sumradius = radius + other.radius; - return Range(std::max(dist - sumradius, (ElemType) 0.0), dist + sumradius); + return RangeType(std::max(dist - sumradius, (ElemType) 0.0), + dist + sumradius); } } @@ -253,10 +252,10 @@ BallBound::RangeDistance( * The difference lies in the way we initialize the ball bound. The way we * expand the bound is same. */ -template +template template -const BallBound& -BallBound::operator|=(const MatType& data) +const BallBound& +BallBound::operator|=(const MatType& data) { if (radius < 0) { @@ -284,9 +283,9 @@ BallBound::operator|=(const MatType& data) } //! Serialize the BallBound. -template +template template -void BallBound::serialize( +void BallBound::serialize( Archive& ar, const uint32_t /* version */) { diff --git a/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp b/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp index 050f26e553..500226524e 100644 --- a/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp +++ b/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp @@ -46,10 +46,11 @@ namespace mlpack { template class BoundType = - HRectBound, - template - class SplitType = MidpointSplit> + template class BoundType = HRectBound, + template class SplitType = MidpointSplit> class BinarySpaceTree { public: @@ -58,7 +59,7 @@ class BinarySpaceTree //! The type of element held in MatType. typedef typename MatType::elem_type ElemType; - typedef SplitType, MatType> Split; + typedef SplitType, MatType> Split; private: //! The left child node. @@ -74,7 +75,7 @@ class BinarySpaceTree //! children). size_t count; //! The bound object for this node. - BoundType bound; + BoundType bound; //! Any extra data contained in the node. StatisticType stat; //! The distance from the centroid of this node to the centroid of the parent. @@ -210,7 +211,8 @@ class BinarySpaceTree BinarySpaceTree(BinarySpaceTree* parent, const size_t begin, const size_t count, - SplitType, MatType>& splitter, + SplitType, MatType>& + splitter, const size_t maxLeafSize = 20); /** @@ -236,7 +238,8 @@ class BinarySpaceTree const size_t begin, const size_t count, std::vector& oldFromNew, - SplitType, MatType>& splitter, + SplitType, MatType>& + splitter, const size_t maxLeafSize = 20); /** @@ -266,7 +269,8 @@ class BinarySpaceTree const size_t count, std::vector& oldFromNew, std::vector& newFromOld, - SplitType, MatType>& splitter, + SplitType, MatType>& + splitter, const size_t maxLeafSize = 20); /** @@ -315,9 +319,9 @@ class BinarySpaceTree ~BinarySpaceTree(); //! Return the bound object for this node. - const BoundType& Bound() const { return bound; } + const BoundType& Bound() const { return bound; } //! Return the bound object for this node. - BoundType& Bound() { return bound; } + BoundType& Bound() { return bound; } //! Return the statistic object for this node. const StatisticType& Stat() const { return stat; } @@ -517,8 +521,9 @@ class BinarySpaceTree * @param maxLeafSize Maximum number of points held in a leaf. * @param splitter Instantiated SplitType object. */ - void SplitNode(const size_t maxLeafSize, - SplitType, MatType>& splitter); + void SplitNode( + const size_t maxLeafSize, + SplitType, MatType>& splitter); /** * Splits the current node, assigning its left and right children recursively. @@ -528,9 +533,10 @@ class BinarySpaceTree * @param maxLeafSize Maximum number of points held in a leaf. * @param splitter Instantiated SplitType object. */ - void SplitNode(std::vector& oldFromNew, - const size_t maxLeafSize, - SplitType, MatType>& splitter); + void SplitNode( + std::vector& oldFromNew, + const size_t maxLeafSize, + SplitType, MatType>& splitter); /** * Update the bound of the current node. This method does not take into @@ -547,7 +553,7 @@ class BinarySpaceTree * * @param boundToUpdate The bound to update. */ - void UpdateBound(HollowBallBound& boundToUpdate); + void UpdateBound(HollowBallBound& boundToUpdate); protected: /** 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 7859364462..74c73d3a25 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 @@ -24,9 +24,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( const MatType& data, @@ -41,7 +43,7 @@ BinarySpaceTree( dataset(new MatType(data)) // Copies the dataset. { // Do the actual splitting of this node. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -51,9 +53,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( const MatType& data, @@ -74,7 +78,7 @@ BinarySpaceTree( oldFromNew[i] = i; // Fill with unharmed indices. // Now do the actual splitting. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(oldFromNew, maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -84,9 +88,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( const MatType& data, @@ -108,7 +114,7 @@ BinarySpaceTree( oldFromNew[i] = i; // Fill with unharmed indices. // Now do the actual splitting. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(oldFromNew, maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -123,9 +129,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree(MatType&& data, const size_t maxLeafSize) : left(NULL), @@ -138,7 +146,7 @@ BinarySpaceTree(MatType&& data, const size_t maxLeafSize) : dataset(new MatType(std::move(data))) { // Do the actual splitting of this node. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -148,9 +156,11 @@ BinarySpaceTree(MatType&& data, const size_t maxLeafSize) : template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( MatType&& data, @@ -171,7 +181,7 @@ BinarySpaceTree( oldFromNew[i] = i; // Fill with unharmed indices. // Now do the actual splitting. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(oldFromNew, maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -181,9 +191,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( MatType&& data, @@ -205,7 +217,7 @@ BinarySpaceTree( oldFromNew[i] = i; // Fill with unharmed indices. // Now do the actual splitting. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(oldFromNew, maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -220,15 +232,17 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( BinarySpaceTree* parent, const size_t begin, const size_t count, - SplitType, MatType>& splitter, + SplitType, MatType>& splitter, const size_t maxLeafSize) : left(NULL), right(NULL), @@ -248,16 +262,18 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( BinarySpaceTree* parent, const size_t begin, const size_t count, std::vector& oldFromNew, - SplitType, MatType>& splitter, + SplitType, MatType>& splitter, const size_t maxLeafSize) : left(NULL), right(NULL), @@ -281,9 +297,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( BinarySpaceTree* parent, @@ -291,7 +309,7 @@ BinarySpaceTree( const size_t count, std::vector& oldFromNew, std::vector& newFromOld, - SplitType, MatType>& splitter, + SplitType, MatType>& splitter, const size_t maxLeafSize) : left(NULL), right(NULL), @@ -324,9 +342,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( const BinarySpaceTree& other) : @@ -384,9 +404,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree& BinarySpaceTree:: operator=(const BinarySpaceTree& other) @@ -456,9 +478,11 @@ operator=(const BinarySpaceTree& other) template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree& BinarySpaceTree:: operator=(BinarySpaceTree&& other) @@ -504,9 +528,11 @@ operator=(BinarySpaceTree&& other) template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree(BinarySpaceTree&& other) : left(other.left), @@ -546,9 +572,11 @@ BinarySpaceTree(BinarySpaceTree&& other) : template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template BinarySpaceTree:: BinarySpaceTree( @@ -569,9 +597,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: ~BinarySpaceTree() { @@ -586,9 +616,11 @@ BinarySpaceTree:: template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline bool BinarySpaceTree::IsLeaf() const { @@ -601,9 +633,11 @@ inline bool BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline size_t BinarySpaceTree::NumChildren() const { @@ -622,9 +656,11 @@ inline size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template size_t BinarySpaceTree::GetNearestChild( @@ -646,9 +682,11 @@ size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template size_t BinarySpaceTree::GetFurthestChild( @@ -670,9 +708,11 @@ size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> size_t BinarySpaceTree::GetNearestChild(const BinarySpaceTree& queryNode) { @@ -695,9 +735,11 @@ size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> size_t BinarySpaceTree::GetFurthestChild(const BinarySpaceTree& queryNode) { @@ -720,9 +762,11 @@ size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline typename BinarySpaceTree::ElemType @@ -746,9 +790,11 @@ BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline typename BinarySpaceTree::ElemType @@ -762,9 +808,11 @@ BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline typename BinarySpaceTree::ElemType @@ -780,9 +828,11 @@ BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline BinarySpaceTree& BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline size_t BinarySpaceTree::NumPoints() const { @@ -818,9 +870,11 @@ inline size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline size_t BinarySpaceTree::NumDescendants() const { @@ -833,9 +887,11 @@ inline size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline size_t BinarySpaceTree::Descendant(const size_t index) const { @@ -848,9 +904,11 @@ inline size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline size_t BinarySpaceTree::Point(const size_t index) const { @@ -860,13 +918,15 @@ inline size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> void BinarySpaceTree:: SplitNode(const size_t maxLeafSize, - SplitType, MatType>& splitter) + SplitType, MatType>& splitter) { // We need to expand the bounds of this node properly. UpdateBound(bound); @@ -927,14 +987,16 @@ BinarySpaceTree:: template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> void BinarySpaceTree:: SplitNode(std::vector& oldFromNew, const size_t maxLeafSize, - SplitType, MatType>& splitter) + SplitType, MatType>& splitter) { // We need to expand the bounds of this node properly. UpdateBound(bound); @@ -996,9 +1058,11 @@ SplitNode(std::vector& oldFromNew, template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: @@ -1011,12 +1075,14 @@ UpdateBound(BoundType2& boundToUpdate) template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> void BinarySpaceTree:: -UpdateBound(HollowBallBound& boundToUpdate) +UpdateBound(HollowBallBound& boundToUpdate) { if (!parent) { @@ -1039,9 +1105,11 @@ UpdateBound(HollowBallBound& boundToUpdate) template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree() : left(NULL), @@ -1063,9 +1131,11 @@ BinarySpaceTree:: template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: diff --git a/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser.hpp b/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser.hpp index 937cb965f1..39766c7afe 100644 --- a/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser.hpp +++ b/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser.hpp @@ -35,9 +35,11 @@ struct QueueFrame template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template class BinarySpaceTree::BreadthFirstDualTreeTraverser diff --git a/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser_impl.hpp b/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser_impl.hpp index 7b598eab6d..8496e7b178 100644 --- a/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser_impl.hpp +++ b/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser_impl.hpp @@ -22,9 +22,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template BinarySpaceTree:: BreadthFirstDualTreeTraverser::BreadthFirstDualTreeTraverser( @@ -50,9 +52,11 @@ bool operator<(const QueueFrame& a, template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: @@ -91,9 +95,11 @@ BreadthFirstDualTreeTraverser::Traverse( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: BreadthFirstDualTreeTraverser::Traverse( diff --git a/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser.hpp b/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser.hpp index fc42d5f15b..c45e4d29d9 100644 --- a/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser.hpp +++ b/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser.hpp @@ -24,9 +24,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template class BinarySpaceTree::DualTreeTraverser diff --git a/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser_impl.hpp b/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser_impl.hpp index ec030e133d..002c9b80c8 100644 --- a/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser_impl.hpp +++ b/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser_impl.hpp @@ -22,9 +22,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template BinarySpaceTree:: DualTreeTraverser::DualTreeTraverser(RuleType& rule) : @@ -38,9 +40,11 @@ DualTreeTraverser::DualTreeTraverser(RuleType& rule) : template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: diff --git a/src/mlpack/core/tree/binary_space_tree/single_tree_traverser.hpp b/src/mlpack/core/tree/binary_space_tree/single_tree_traverser.hpp index 1fe477c907..9616c9e3ab 100644 --- a/src/mlpack/core/tree/binary_space_tree/single_tree_traverser.hpp +++ b/src/mlpack/core/tree/binary_space_tree/single_tree_traverser.hpp @@ -23,9 +23,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template class BinarySpaceTree::SingleTreeTraverser diff --git a/src/mlpack/core/tree/binary_space_tree/single_tree_traverser_impl.hpp b/src/mlpack/core/tree/binary_space_tree/single_tree_traverser_impl.hpp index cb164d8e39..9b783b0e2a 100644 --- a/src/mlpack/core/tree/binary_space_tree/single_tree_traverser_impl.hpp +++ b/src/mlpack/core/tree/binary_space_tree/single_tree_traverser_impl.hpp @@ -24,9 +24,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template BinarySpaceTree:: SingleTreeTraverser::SingleTreeTraverser(RuleType& rule) : @@ -37,9 +39,11 @@ SingleTreeTraverser::SingleTreeTraverser(RuleType& rule) : template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: diff --git a/src/mlpack/core/tree/binary_space_tree/traits.hpp b/src/mlpack/core/tree/binary_space_tree/traits.hpp index 623cddf5c3..4d563c6bba 100644 --- a/src/mlpack/core/tree/binary_space_tree/traits.hpp +++ b/src/mlpack/core/tree/binary_space_tree/traits.hpp @@ -26,9 +26,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> class TreeTraits> { @@ -80,7 +82,9 @@ class TreeTraits class BoundType> + template class BoundType> class TreeTraits> { @@ -130,9 +134,11 @@ class TreeTraits class BoundType> -class TreeTraits> + template class BoundType> +class TreeTraits> { public: /** diff --git a/src/mlpack/core/tree/space_split/projection_vector.hpp b/src/mlpack/core/tree/space_split/projection_vector.hpp index ee2e3fea03..8dfc0de532 100644 --- a/src/mlpack/core/tree/space_split/projection_vector.hpp +++ b/src/mlpack/core/tree/space_split/projection_vector.hpp @@ -67,9 +67,9 @@ class AxisParallelProjVector * @param bound Bound to be projected. * @return Range of projected values. */ - template - RangeType Project( - const BallBound& bound) const + template + RangeType Project( + const BallBound& bound) const { return bound[dim]; } @@ -128,11 +128,10 @@ class ProjVector * @param bound Bound to be projected. * @return Range of projected values. */ - template - RangeType Project( - const BallBound& bound) const + template + RangeType Project( + const BallBound& bound) const { - typedef typename VecType::elem_type ElemType; const double center = Project(bound.Center()); const ElemType radius = bound.Radius(); return RangeType(center - radius, center + radius); diff --git a/src/mlpack/tests/dbscan_test.cpp b/src/mlpack/tests/dbscan_test.cpp index 0c5617d87b..eabf4e4aa0 100644 --- a/src/mlpack/tests/dbscan_test.cpp +++ b/src/mlpack/tests/dbscan_test.cpp @@ -17,18 +17,6 @@ using namespace mlpack; -/** - * A couple of handful declarations for float32 testing. - * These will be removed when we refactor the Bounds to accept MatType. - * For now, we will keep the following declarations. - */ -template -using FloatHRectBound = HRectBound; - -template -using FloatKDTree = BinarySpaceTree; - TEST_CASE("OneClusterTest", "[DBSCANTest]") { // Make sure that if we have points in the unit box, and if we set epsilon @@ -229,7 +217,7 @@ TEST_CASE("Float32OutlierSingleModeTest", "[DBSCANTest]") DBSCAN, - FloatKDTree>, + KDTree>, OrderedPointSelection> d(0.1, 3, false); arma::Row assignments; diff --git a/src/mlpack/tests/knn_test.cpp b/src/mlpack/tests/knn_test.cpp index 28a2cb59b2..b238fa6309 100644 --- a/src/mlpack/tests/knn_test.cpp +++ b/src/mlpack/tests/knn_test.cpp @@ -16,18 +16,6 @@ using namespace mlpack; -/** - * A couple of handful declarations for float32 testing. - * These will be removed when we refactor the Bounds to accept MatType. - * For now, we will keep the following declarations. - */ -template -using FloatHRectBound = HRectBound; - -template -using FloatKDTree = BinarySpaceTree; - /** * Test that Unmap() works in the dual-tree case (see unmap.hpp). */ @@ -777,14 +765,12 @@ TEST_CASE("KNNSingleTreeVsNaiveF32", "[KNNTest]") NeighborSearch knn(dataset, SINGLE_TREE_MODE); + arma::fmat> knn(dataset, SINGLE_TREE_MODE); // Set up computation for naive mode. NeighborSearch naive(dataset, NAIVE_MODE); + arma::fmat> naive(dataset, NAIVE_MODE); arma::Mat neighborsTree; arma::fmat distancesTree; diff --git a/src/mlpack/tests/serialization_test.cpp b/src/mlpack/tests/serialization_test.cpp index 1433c9d9c6..d12235f9b5 100644 --- a/src/mlpack/tests/serialization_test.cpp +++ b/src/mlpack/tests/serialization_test.cpp @@ -152,12 +152,12 @@ TEST_CASE("BallBoundTest", "[SerializationTest]") TEST_CASE("MahalanobisBallBoundTest", "[SerializationTest]") { - BallBound, arma::vec> b(100); + BallBound, double, arma::vec> b(100); b.Center().randu(); b.Radius() = 14.0; b.Distance().Q().randu(100, 100); - BallBound, arma::vec> xmlB, jsonB, binaryB; + BallBound, double, arma::vec> xmlB, jsonB, binaryB; SerializeObjectAll(b, xmlB, jsonB, binaryB); From f9e89a008a0d1e3f907089e6d201f32499ed89d7 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 6 Jul 2024 11:19:44 -0400 Subject: [PATCH 085/212] Refactor BoundType to take ElemType as a second parameter. --- src/mlpack/core/tree/ballbound.hpp | 5 +- src/mlpack/core/tree/ballbound_impl.hpp | 109 ++++--- .../binary_space_tree/binary_space_tree.hpp | 40 ++- .../binary_space_tree_impl.hpp | 304 +++++++++++------- .../breadth_first_dual_tree_traverser.hpp | 8 +- ...breadth_first_dual_tree_traverser_impl.hpp | 24 +- .../binary_space_tree/dual_tree_traverser.hpp | 8 +- .../dual_tree_traverser_impl.hpp | 16 +- .../single_tree_traverser.hpp | 8 +- .../single_tree_traverser_impl.hpp | 16 +- .../core/tree/binary_space_tree/traits.hpp | 20 +- .../tree/space_split/projection_vector.hpp | 13 +- src/mlpack/tests/dbscan_test.cpp | 14 +- src/mlpack/tests/knn_test.cpp | 18 +- src/mlpack/tests/serialization_test.cpp | 4 +- 15 files changed, 340 insertions(+), 267 deletions(-) diff --git a/src/mlpack/core/tree/ballbound.hpp b/src/mlpack/core/tree/ballbound.hpp index 0f1f8f9636..9a7e5f1977 100644 --- a/src/mlpack/core/tree/ballbound.hpp +++ b/src/mlpack/core/tree/ballbound.hpp @@ -27,12 +27,11 @@ namespace mlpack { * @tparam VecType Type of vector (arma::vec or arma::sp_vec or similar). */ template, - typename VecType = arma::vec> + typename ElemType = double, + typename VecType = arma::Col> class BallBound { public: - //! The underlying data type. - typedef typename VecType::elem_type ElemType; //! A public version of the vector type. typedef VecType Vec; diff --git a/src/mlpack/core/tree/ballbound_impl.hpp b/src/mlpack/core/tree/ballbound_impl.hpp index 123f0200cb..8793c3077c 100644 --- a/src/mlpack/core/tree/ballbound_impl.hpp +++ b/src/mlpack/core/tree/ballbound_impl.hpp @@ -20,8 +20,8 @@ namespace mlpack { //! Empty Constructor. -template -BallBound::BallBound() : +template +BallBound::BallBound() : radius(std::numeric_limits::lowest()), distance(new DistanceType()), ownsDistance(true) @@ -32,8 +32,8 @@ BallBound::BallBound() : * * @param dimension Dimensionality of ball bound. */ -template -BallBound::BallBound(const size_t dimension) : +template +BallBound::BallBound(const size_t dimension) : radius(std::numeric_limits::lowest()), center(dimension), distance(new DistanceType()), @@ -46,9 +46,9 @@ BallBound::BallBound(const size_t dimension) : * @param radius Radius of ball bound. * @param center Center of ball bound. */ -template -BallBound::BallBound(const ElemType radius, - const VecType& center) : +template +BallBound::BallBound(const ElemType radius, + const VecType& center) : radius(radius), center(center), distance(new DistanceType()), @@ -56,8 +56,8 @@ BallBound::BallBound(const ElemType radius, { /* Nothing to do. */ } //! Copy Constructor. To prevent memory leaks. -template -BallBound::BallBound(const BallBound& other) : +template +BallBound::BallBound(const BallBound& other) : radius(other.radius), center(other.center), distance(other.distance), @@ -65,8 +65,9 @@ BallBound::BallBound(const BallBound& other) : { /* Nothing to do. */ } //! For the same reason as the copy constructor: to prevent memory leaks. -template -BallBound& BallBound::operator=( +template +BallBound& +BallBound::operator=( const BallBound& other) { if (this != &other) @@ -80,8 +81,8 @@ BallBound& BallBound::operator=( } //! Move constructor. -template -BallBound::BallBound(BallBound&& other) : +template +BallBound::BallBound(BallBound&& other) : radius(other.radius), center(other.center), distance(other.distance), @@ -95,8 +96,9 @@ BallBound::BallBound(BallBound&& other) : } //! Move assignment operator. -template -BallBound& BallBound::operator=( +template +BallBound& +BallBound::operator=( BallBound&& other) { if (this != &other) @@ -115,29 +117,30 @@ BallBound& BallBound::operator=( } //! Destructor to release allocated memory. -template -BallBound::~BallBound() +template +BallBound::~BallBound() { if (ownsDistance) delete distance; } //! Get the range in a certain dimension. -template -RangeType::ElemType> -BallBound::operator[](const size_t i) const +template +RangeType +BallBound::operator[](const size_t i) const { if (radius < 0) - return Range(); + return RangeType(); else - return Range(center[i] - radius, center[i] + radius); + return RangeType(center[i] - radius, center[i] + radius); } /** * Determines if a point is within the bound. */ -template -bool BallBound::Contains(const VecType& point) const +template +bool BallBound::Contains(const VecType& point) + const { if (radius < 0) return false; @@ -148,10 +151,9 @@ bool BallBound::Contains(const VecType& point) const /** * Calculates minimum bound-to-point squared distance. */ -template +template template -typename BallBound::ElemType -BallBound::MinDistance( +ElemType BallBound::MinDistance( const OtherVecType& point, typename std::enable_if_t::value>* /* junk */) const { @@ -164,10 +166,9 @@ BallBound::MinDistance( /** * Calculates minimum bound-to-bound squared distance. */ -template -typename BallBound::ElemType -BallBound::MinDistance(const BallBound& other) - const +template +ElemType BallBound::MinDistance( + const BallBound& other) const { if (radius < 0) return std::numeric_limits::max(); @@ -182,10 +183,9 @@ BallBound::MinDistance(const BallBound& other) /** * Computes maximum distance. */ -template +template template -typename BallBound::ElemType -BallBound::MaxDistance( +ElemType BallBound::MaxDistance( const OtherVecType& point, typename std::enable_if_t::value>* /* junk */) const { @@ -198,10 +198,9 @@ BallBound::MaxDistance( /** * Computes maximum distance. */ -template -typename BallBound::ElemType -BallBound::MaxDistance(const BallBound& other) - const +template +ElemType BallBound::MaxDistance( + const BallBound& other) const { if (radius < 0) return std::numeric_limits::max(); @@ -214,36 +213,36 @@ BallBound::MaxDistance(const BallBound& other) * * Example: bound1.MinDistanceSq(other) for minimum squared distance. */ -template +template template -RangeType::ElemType> -BallBound::RangeDistance( +RangeType BallBound::RangeDistance( const OtherVecType& point, typename std::enable_if_t::value>* /* junk */) const { if (radius < 0) - return Range(std::numeric_limits::max(), - std::numeric_limits::max()); + return RangeType(std::numeric_limits::max(), + std::numeric_limits::max()); else { const ElemType dist = distance->Evaluate(center, point); - return Range(std::max(dist - radius, (ElemType) 0.0), dist + radius); + return RangeType(std::max(dist - radius, (ElemType) 0.0), + dist + radius); } } -template -RangeType::ElemType> -BallBound::RangeDistance( +template +RangeType BallBound::RangeDistance( const BallBound& other) const { if (radius < 0) - return Range(std::numeric_limits::max(), - std::numeric_limits::max()); + return RangeType(std::numeric_limits::max(), + std::numeric_limits::max()); else { const ElemType dist = distance->Evaluate(center, other.center); const ElemType sumradius = radius + other.radius; - return Range(std::max(dist - sumradius, (ElemType) 0.0), dist + sumradius); + return RangeType(std::max(dist - sumradius, (ElemType) 0.0), + dist + sumradius); } } @@ -253,10 +252,10 @@ BallBound::RangeDistance( * The difference lies in the way we initialize the ball bound. The way we * expand the bound is same. */ -template +template template -const BallBound& -BallBound::operator|=(const MatType& data) +const BallBound& +BallBound::operator|=(const MatType& data) { if (radius < 0) { @@ -284,9 +283,9 @@ BallBound::operator|=(const MatType& data) } //! Serialize the BallBound. -template +template template -void BallBound::serialize( +void BallBound::serialize( Archive& ar, const uint32_t /* version */) { diff --git a/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp b/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp index 050f26e553..500226524e 100644 --- a/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp +++ b/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp @@ -46,10 +46,11 @@ namespace mlpack { template class BoundType = - HRectBound, - template - class SplitType = MidpointSplit> + template class BoundType = HRectBound, + template class SplitType = MidpointSplit> class BinarySpaceTree { public: @@ -58,7 +59,7 @@ class BinarySpaceTree //! The type of element held in MatType. typedef typename MatType::elem_type ElemType; - typedef SplitType, MatType> Split; + typedef SplitType, MatType> Split; private: //! The left child node. @@ -74,7 +75,7 @@ class BinarySpaceTree //! children). size_t count; //! The bound object for this node. - BoundType bound; + BoundType bound; //! Any extra data contained in the node. StatisticType stat; //! The distance from the centroid of this node to the centroid of the parent. @@ -210,7 +211,8 @@ class BinarySpaceTree BinarySpaceTree(BinarySpaceTree* parent, const size_t begin, const size_t count, - SplitType, MatType>& splitter, + SplitType, MatType>& + splitter, const size_t maxLeafSize = 20); /** @@ -236,7 +238,8 @@ class BinarySpaceTree const size_t begin, const size_t count, std::vector& oldFromNew, - SplitType, MatType>& splitter, + SplitType, MatType>& + splitter, const size_t maxLeafSize = 20); /** @@ -266,7 +269,8 @@ class BinarySpaceTree const size_t count, std::vector& oldFromNew, std::vector& newFromOld, - SplitType, MatType>& splitter, + SplitType, MatType>& + splitter, const size_t maxLeafSize = 20); /** @@ -315,9 +319,9 @@ class BinarySpaceTree ~BinarySpaceTree(); //! Return the bound object for this node. - const BoundType& Bound() const { return bound; } + const BoundType& Bound() const { return bound; } //! Return the bound object for this node. - BoundType& Bound() { return bound; } + BoundType& Bound() { return bound; } //! Return the statistic object for this node. const StatisticType& Stat() const { return stat; } @@ -517,8 +521,9 @@ class BinarySpaceTree * @param maxLeafSize Maximum number of points held in a leaf. * @param splitter Instantiated SplitType object. */ - void SplitNode(const size_t maxLeafSize, - SplitType, MatType>& splitter); + void SplitNode( + const size_t maxLeafSize, + SplitType, MatType>& splitter); /** * Splits the current node, assigning its left and right children recursively. @@ -528,9 +533,10 @@ class BinarySpaceTree * @param maxLeafSize Maximum number of points held in a leaf. * @param splitter Instantiated SplitType object. */ - void SplitNode(std::vector& oldFromNew, - const size_t maxLeafSize, - SplitType, MatType>& splitter); + void SplitNode( + std::vector& oldFromNew, + const size_t maxLeafSize, + SplitType, MatType>& splitter); /** * Update the bound of the current node. This method does not take into @@ -547,7 +553,7 @@ class BinarySpaceTree * * @param boundToUpdate The bound to update. */ - void UpdateBound(HollowBallBound& boundToUpdate); + void UpdateBound(HollowBallBound& boundToUpdate); protected: /** 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 7859364462..74c73d3a25 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 @@ -24,9 +24,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( const MatType& data, @@ -41,7 +43,7 @@ BinarySpaceTree( dataset(new MatType(data)) // Copies the dataset. { // Do the actual splitting of this node. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -51,9 +53,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( const MatType& data, @@ -74,7 +78,7 @@ BinarySpaceTree( oldFromNew[i] = i; // Fill with unharmed indices. // Now do the actual splitting. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(oldFromNew, maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -84,9 +88,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( const MatType& data, @@ -108,7 +114,7 @@ BinarySpaceTree( oldFromNew[i] = i; // Fill with unharmed indices. // Now do the actual splitting. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(oldFromNew, maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -123,9 +129,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree(MatType&& data, const size_t maxLeafSize) : left(NULL), @@ -138,7 +146,7 @@ BinarySpaceTree(MatType&& data, const size_t maxLeafSize) : dataset(new MatType(std::move(data))) { // Do the actual splitting of this node. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -148,9 +156,11 @@ BinarySpaceTree(MatType&& data, const size_t maxLeafSize) : template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( MatType&& data, @@ -171,7 +181,7 @@ BinarySpaceTree( oldFromNew[i] = i; // Fill with unharmed indices. // Now do the actual splitting. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(oldFromNew, maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -181,9 +191,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( MatType&& data, @@ -205,7 +217,7 @@ BinarySpaceTree( oldFromNew[i] = i; // Fill with unharmed indices. // Now do the actual splitting. - SplitType, MatType> splitter; + SplitType, MatType> splitter; SplitNode(oldFromNew, maxLeafSize, splitter); // Create the statistic depending on if we are a leaf or not. @@ -220,15 +232,17 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( BinarySpaceTree* parent, const size_t begin, const size_t count, - SplitType, MatType>& splitter, + SplitType, MatType>& splitter, const size_t maxLeafSize) : left(NULL), right(NULL), @@ -248,16 +262,18 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( BinarySpaceTree* parent, const size_t begin, const size_t count, std::vector& oldFromNew, - SplitType, MatType>& splitter, + SplitType, MatType>& splitter, const size_t maxLeafSize) : left(NULL), right(NULL), @@ -281,9 +297,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( BinarySpaceTree* parent, @@ -291,7 +309,7 @@ BinarySpaceTree( const size_t count, std::vector& oldFromNew, std::vector& newFromOld, - SplitType, MatType>& splitter, + SplitType, MatType>& splitter, const size_t maxLeafSize) : left(NULL), right(NULL), @@ -324,9 +342,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree( const BinarySpaceTree& other) : @@ -384,9 +404,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree& BinarySpaceTree:: operator=(const BinarySpaceTree& other) @@ -456,9 +478,11 @@ operator=(const BinarySpaceTree& other) template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree& BinarySpaceTree:: operator=(BinarySpaceTree&& other) @@ -504,9 +528,11 @@ operator=(BinarySpaceTree&& other) template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree(BinarySpaceTree&& other) : left(other.left), @@ -546,9 +572,11 @@ BinarySpaceTree(BinarySpaceTree&& other) : template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template BinarySpaceTree:: BinarySpaceTree( @@ -569,9 +597,11 @@ BinarySpaceTree( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: ~BinarySpaceTree() { @@ -586,9 +616,11 @@ BinarySpaceTree:: template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline bool BinarySpaceTree::IsLeaf() const { @@ -601,9 +633,11 @@ inline bool BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline size_t BinarySpaceTree::NumChildren() const { @@ -622,9 +656,11 @@ inline size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template size_t BinarySpaceTree::GetNearestChild( @@ -646,9 +682,11 @@ size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template size_t BinarySpaceTree::GetFurthestChild( @@ -670,9 +708,11 @@ size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> size_t BinarySpaceTree::GetNearestChild(const BinarySpaceTree& queryNode) { @@ -695,9 +735,11 @@ size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> size_t BinarySpaceTree::GetFurthestChild(const BinarySpaceTree& queryNode) { @@ -720,9 +762,11 @@ size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline typename BinarySpaceTree::ElemType @@ -746,9 +790,11 @@ BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline typename BinarySpaceTree::ElemType @@ -762,9 +808,11 @@ BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline typename BinarySpaceTree::ElemType @@ -780,9 +828,11 @@ BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline BinarySpaceTree& BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline size_t BinarySpaceTree::NumPoints() const { @@ -818,9 +870,11 @@ inline size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline size_t BinarySpaceTree::NumDescendants() const { @@ -833,9 +887,11 @@ inline size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline size_t BinarySpaceTree::Descendant(const size_t index) const { @@ -848,9 +904,11 @@ inline size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> inline size_t BinarySpaceTree::Point(const size_t index) const { @@ -860,13 +918,15 @@ inline size_t BinarySpaceTree class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> void BinarySpaceTree:: SplitNode(const size_t maxLeafSize, - SplitType, MatType>& splitter) + SplitType, MatType>& splitter) { // We need to expand the bounds of this node properly. UpdateBound(bound); @@ -927,14 +987,16 @@ BinarySpaceTree:: template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> void BinarySpaceTree:: SplitNode(std::vector& oldFromNew, const size_t maxLeafSize, - SplitType, MatType>& splitter) + SplitType, MatType>& splitter) { // We need to expand the bounds of this node properly. UpdateBound(bound); @@ -996,9 +1058,11 @@ SplitNode(std::vector& oldFromNew, template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: @@ -1011,12 +1075,14 @@ UpdateBound(BoundType2& boundToUpdate) template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> void BinarySpaceTree:: -UpdateBound(HollowBallBound& boundToUpdate) +UpdateBound(HollowBallBound& boundToUpdate) { if (!parent) { @@ -1039,9 +1105,11 @@ UpdateBound(HollowBallBound& boundToUpdate) template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> BinarySpaceTree:: BinarySpaceTree() : left(NULL), @@ -1063,9 +1131,11 @@ BinarySpaceTree:: template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: diff --git a/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser.hpp b/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser.hpp index 937cb965f1..39766c7afe 100644 --- a/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser.hpp +++ b/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser.hpp @@ -35,9 +35,11 @@ struct QueueFrame template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template class BinarySpaceTree::BreadthFirstDualTreeTraverser diff --git a/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser_impl.hpp b/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser_impl.hpp index 7b598eab6d..8496e7b178 100644 --- a/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser_impl.hpp +++ b/src/mlpack/core/tree/binary_space_tree/breadth_first_dual_tree_traverser_impl.hpp @@ -22,9 +22,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template BinarySpaceTree:: BreadthFirstDualTreeTraverser::BreadthFirstDualTreeTraverser( @@ -50,9 +52,11 @@ bool operator<(const QueueFrame& a, template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: @@ -91,9 +95,11 @@ BreadthFirstDualTreeTraverser::Traverse( template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: BreadthFirstDualTreeTraverser::Traverse( diff --git a/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser.hpp b/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser.hpp index fc42d5f15b..c45e4d29d9 100644 --- a/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser.hpp +++ b/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser.hpp @@ -24,9 +24,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template class BinarySpaceTree::DualTreeTraverser diff --git a/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser_impl.hpp b/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser_impl.hpp index ec030e133d..002c9b80c8 100644 --- a/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser_impl.hpp +++ b/src/mlpack/core/tree/binary_space_tree/dual_tree_traverser_impl.hpp @@ -22,9 +22,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template BinarySpaceTree:: DualTreeTraverser::DualTreeTraverser(RuleType& rule) : @@ -38,9 +40,11 @@ DualTreeTraverser::DualTreeTraverser(RuleType& rule) : template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: diff --git a/src/mlpack/core/tree/binary_space_tree/single_tree_traverser.hpp b/src/mlpack/core/tree/binary_space_tree/single_tree_traverser.hpp index 1fe477c907..9616c9e3ab 100644 --- a/src/mlpack/core/tree/binary_space_tree/single_tree_traverser.hpp +++ b/src/mlpack/core/tree/binary_space_tree/single_tree_traverser.hpp @@ -23,9 +23,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template class BinarySpaceTree::SingleTreeTraverser diff --git a/src/mlpack/core/tree/binary_space_tree/single_tree_traverser_impl.hpp b/src/mlpack/core/tree/binary_space_tree/single_tree_traverser_impl.hpp index cb164d8e39..9b783b0e2a 100644 --- a/src/mlpack/core/tree/binary_space_tree/single_tree_traverser_impl.hpp +++ b/src/mlpack/core/tree/binary_space_tree/single_tree_traverser_impl.hpp @@ -24,9 +24,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template BinarySpaceTree:: SingleTreeTraverser::SingleTreeTraverser(RuleType& rule) : @@ -37,9 +39,11 @@ SingleTreeTraverser::SingleTreeTraverser(RuleType& rule) : template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> template void BinarySpaceTree:: diff --git a/src/mlpack/core/tree/binary_space_tree/traits.hpp b/src/mlpack/core/tree/binary_space_tree/traits.hpp index 623cddf5c3..4d563c6bba 100644 --- a/src/mlpack/core/tree/binary_space_tree/traits.hpp +++ b/src/mlpack/core/tree/binary_space_tree/traits.hpp @@ -26,9 +26,11 @@ namespace mlpack { template class BoundType, - template - class SplitType> + template class BoundType, + template class SplitType> class TreeTraits> { @@ -80,7 +82,9 @@ class TreeTraits class BoundType> + template class BoundType> class TreeTraits> { @@ -130,9 +134,11 @@ class TreeTraits class BoundType> -class TreeTraits> + template class BoundType> +class TreeTraits> { public: /** diff --git a/src/mlpack/core/tree/space_split/projection_vector.hpp b/src/mlpack/core/tree/space_split/projection_vector.hpp index ee2e3fea03..8dfc0de532 100644 --- a/src/mlpack/core/tree/space_split/projection_vector.hpp +++ b/src/mlpack/core/tree/space_split/projection_vector.hpp @@ -67,9 +67,9 @@ class AxisParallelProjVector * @param bound Bound to be projected. * @return Range of projected values. */ - template - RangeType Project( - const BallBound& bound) const + template + RangeType Project( + const BallBound& bound) const { return bound[dim]; } @@ -128,11 +128,10 @@ class ProjVector * @param bound Bound to be projected. * @return Range of projected values. */ - template - RangeType Project( - const BallBound& bound) const + template + RangeType Project( + const BallBound& bound) const { - typedef typename VecType::elem_type ElemType; const double center = Project(bound.Center()); const ElemType radius = bound.Radius(); return RangeType(center - radius, center + radius); diff --git a/src/mlpack/tests/dbscan_test.cpp b/src/mlpack/tests/dbscan_test.cpp index 0c5617d87b..eabf4e4aa0 100644 --- a/src/mlpack/tests/dbscan_test.cpp +++ b/src/mlpack/tests/dbscan_test.cpp @@ -17,18 +17,6 @@ using namespace mlpack; -/** - * A couple of handful declarations for float32 testing. - * These will be removed when we refactor the Bounds to accept MatType. - * For now, we will keep the following declarations. - */ -template -using FloatHRectBound = HRectBound; - -template -using FloatKDTree = BinarySpaceTree; - TEST_CASE("OneClusterTest", "[DBSCANTest]") { // Make sure that if we have points in the unit box, and if we set epsilon @@ -229,7 +217,7 @@ TEST_CASE("Float32OutlierSingleModeTest", "[DBSCANTest]") DBSCAN, - FloatKDTree>, + KDTree>, OrderedPointSelection> d(0.1, 3, false); arma::Row assignments; diff --git a/src/mlpack/tests/knn_test.cpp b/src/mlpack/tests/knn_test.cpp index 28a2cb59b2..b238fa6309 100644 --- a/src/mlpack/tests/knn_test.cpp +++ b/src/mlpack/tests/knn_test.cpp @@ -16,18 +16,6 @@ using namespace mlpack; -/** - * A couple of handful declarations for float32 testing. - * These will be removed when we refactor the Bounds to accept MatType. - * For now, we will keep the following declarations. - */ -template -using FloatHRectBound = HRectBound; - -template -using FloatKDTree = BinarySpaceTree; - /** * Test that Unmap() works in the dual-tree case (see unmap.hpp). */ @@ -777,14 +765,12 @@ TEST_CASE("KNNSingleTreeVsNaiveF32", "[KNNTest]") NeighborSearch knn(dataset, SINGLE_TREE_MODE); + arma::fmat> knn(dataset, SINGLE_TREE_MODE); // Set up computation for naive mode. NeighborSearch naive(dataset, NAIVE_MODE); + arma::fmat> naive(dataset, NAIVE_MODE); arma::Mat neighborsTree; arma::fmat distancesTree; diff --git a/src/mlpack/tests/serialization_test.cpp b/src/mlpack/tests/serialization_test.cpp index 1433c9d9c6..d12235f9b5 100644 --- a/src/mlpack/tests/serialization_test.cpp +++ b/src/mlpack/tests/serialization_test.cpp @@ -152,12 +152,12 @@ TEST_CASE("BallBoundTest", "[SerializationTest]") TEST_CASE("MahalanobisBallBoundTest", "[SerializationTest]") { - BallBound, arma::vec> b(100); + BallBound, double, arma::vec> b(100); b.Center().randu(); b.Radius() = 14.0; b.Distance().Q().randu(100, 100); - BallBound, arma::vec> xmlB, jsonB, binaryB; + BallBound, double, arma::vec> xmlB, jsonB, binaryB; SerializeObjectAll(b, xmlB, jsonB, binaryB); From 57d2e8b21e8350b6be5e4754994db980254584f0 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 6 Jul 2024 12:23:44 -0400 Subject: [PATCH 086/212] Handle older versions of numpy properly. --- src/mlpack/bindings/python/mlpack/matrix_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mlpack/bindings/python/mlpack/matrix_utils.py b/src/mlpack/bindings/python/mlpack/matrix_utils.py index 294896b28d..3cf39126d5 100644 --- a/src/mlpack/bindings/python/mlpack/matrix_utils.py +++ b/src/mlpack/bindings/python/mlpack/matrix_utils.py @@ -160,10 +160,10 @@ def to_matrix_with_info(x, dtype, copy=False): dims = len(x) d = np.zeros([dims]) - if copy: - out = np.array(x, dtype=dtype, copy=True) + if np.lib.NumpyVersion(np.__version__) >= '2.0.0b1': + out = np.array(x, dtype=dtype, copy=(True if copy else None)) else: - out = np.array(x, dtype=dtype, copy=None) + out = np.array(x, dtype=dtype, copy=copy) # Since we don't have a great way to check if these are using the same # memory location, we will probe manually (ugh). From 83af6c5adba079705ee0d86fc245f0661cf6abde Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 6 Jul 2024 12:39:47 -0400 Subject: [PATCH 087/212] Fix NCA documentation issues and clean up source. --- doc/user/methods/nca.md | 107 +++++++++++++----- .../methods/nca/first_element_is_arma.hpp | 44 +++++++ src/mlpack/methods/nca/nca.hpp | 54 ++++++--- src/mlpack/methods/nca/nca_impl.hpp | 1 + .../nca/nca_softmax_error_function_impl.hpp | 5 - 5 files changed, 163 insertions(+), 48 deletions(-) create mode 100644 src/mlpack/methods/nca/first_element_is_arma.hpp diff --git a/doc/user/methods/nca.md b/doc/user/methods/nca.md index c46c5b2584..4870907af8 100644 --- a/doc/user/methods/nca.md +++ b/doc/user/methods/nca.md @@ -8,7 +8,8 @@ classification performance. Note that `NCA` is a computationally intensive technique (each optimization iteration takes time quadratic in the data size!), and may be slow to run even -for datasets of only moderate size. +for datasets of only moderate size. See [`LMNN`](lmnn.md) for another distance +learning technique that scales better to larger datasets. #### Simple usage example: @@ -29,7 +30,8 @@ nca.LearnDistance(dataset, labels, distance); // Step 2: learn distance. arma::mat transformedData = distance * dataset; // Or, you can create a MahalanobisDistance to evaluate points in the // transformed dataset space. -mlpack::MahalanobisDistance d(distance); +arma::mat q = distance.t() * distance; +mlpack::MahalanobisDistance d(std::move(q)); std::cout << "Distance between points 0 and 1:" << std::endl; std::cout << " - Before NCA: " @@ -52,7 +54,7 @@ std::cout << " - After NCA: " - * [mlpack distance metrics](core.md#metrics) + * [mlpack distance metrics](../core.md#distances) * [`LMNN`](lmnn.md) * [Metric learning on Wikipedia](https://en.wikipedia.org/wiki/Similarity_learning#Metric_learning) * [Neighborhood Components Analysis on Wikipedia](https://en.wikipedia.org/wiki/Neighbourhood_components_analysis) @@ -67,7 +69,8 @@ std::cout << " - After NCA: " * `nca = NCA()` * `nca = NCA(distance)` - - Create an `NCA` object using a custom [`DistanceType`](core.md#metrics). + - Create an `NCA` object using a custom + [`DistanceType`](../core.md#distances). - An instantiated `DistanceType` can optionally be passed with the `distance` parameter. - Using a custom `DistanceType` means that `LearnDistance()` will learn a @@ -101,20 +104,29 @@ to learn a distance. [ensmallen optimizer](https://www.ensmallen.org) and/or [ensmallen callbacks](https://www.ensmallen.org/docs.html#callback-documentation) to be used for the learning process. - - `distance` will be set to size `data.n_rows` x `data.n_rows`. + - If `distance` already has size `r` x `data.n_rows` for some `r` less than + or equal to `data.n_rows`, it will be used as the starting point for + optimization. Otherwise, the identity matrix with size `data.n_rows` x + `data.n_rows` will be used. + - When optimization is complete, `distance` will have size `r` x + `data.n_rows`, where `r` is less than or equal to `data.n_rows`. + * *Note*: If `r < data.n_rows`, then NCA has learned a distance metric that + also reduces the dimensionality of the data. See the + [last example](#simple-examples). To use `distance`, either: * Compute a new transformed dataset as `distance * data`, or * Use an instantiated [`MahalanobisDistance`](../core.md#mahalanobisdistance) - with `distance` as the `Q` matrix. + with `distance.t() * distance` as the `Q` matrix. See the [examples section](#simple-examples) for more details. ***Caveat:*** NCA operates by repeatedly computing expressions of the form `exp(-distance.Evaluate(data.col(i), data.col(j)))` (that is, the exponential of the negative distance between two points). When distances are very large, this -*quantity underflows to 0* and results will not be reasonable. +***quantity underflows to 0*** and results will not be reasonable. + - This situation can be detected, usually by a result where `distance` is equal to the identity matrix. - Alternately, if the [`ens::ProgressBar()` @@ -122,7 +134,7 @@ the negative distance between two points). When distances are very large, this 0 often means this situation has occurred. - To mitigate the problem, consider scaling data such that the maximum pairwise distance is less than 10. See the [simple examples](#simple-examples) that - use the `satellite` dataset. + use the `vehicle` dataset. #### `LearnDistance()` Parameters: @@ -131,6 +143,8 @@ the negative distance between two points). When distances are very large, this | `data` | [`arma::mat`](../matrices.md) | [Column-major](../matrices.md#representing-data-in-mlpack) training matrix. | | `labels` | [`arma::Row`](../matrices.md) | Training labels, [between `0` and `numClasses - 1`](../load_save.md#normalizing-labels) (inclusive). Should have length `data.n_cols`. | | `distance` | [`arma::mat`](../matrices.md) | Output matrix to store transformation matrix representing learned distance. | +| `optimizer` | [any ensmallen optimizer](https://www.ensmallen.org) | Instantiated ensmallen optimizer for [differentiable functions](https://www.ensmallen.org/docs.html#differentiable-functions) or [differentiable separable functions](https://www.ensmallen.org/docs.html#differentiable-separable-functions). | `ens::StandardSGD()` | +| `callbacks...` | [any set of ensmallen callbacks](https://www.ensmallen.org/docs.html#callback-documentation) | Optional callbacks for the ensmallen optimizer, such as e.g. `ens::ProgressBar()`, `ens::Report()`, or others. | _(N/A)_ | ***Note***: any matrix type can be used for `data` and `distance`, so long as that type implements the Armadillo API. So, e.g., `arma::fmat` can be used. @@ -151,7 +165,7 @@ that type implements the Armadillo API. So, e.g., `arma::fmat` can be used. Learn a distance metric to improve classification performance on the iris dataset, and show improved performance when using -[`NaiveBayesClassifier`](nbc.md). +[`NaiveBayesClassifier`](naive_bayes_classifier.md). ```c++ // See https://datasets.mlpack.org/iris.csv. @@ -189,8 +203,8 @@ std::cout << "Naive Bayes Classifier with NCA: " --- -Learn a distance metric on the satellite dataset, using 32-bit floating point to -represent the data and metric. +Learn a distance metric on the ionosphere dataset, using 32-bit floating point +to represent the data and metric. ```c++ // See https://datasets.mlpack.org/ionosphere.csv. @@ -203,13 +217,15 @@ arma::Row labels = dataset.shed_row(dataset.n_rows - 1); // Create an NCA object and learn distance on float32 data. -// Pass a progress bar callback, and a configured SGD optimizer that reduces the -// number of epochs to 3 (so this example runs quickly; more would be required -// in most real-world situations!). +// To keep computation time down, we use an instantiated optimizer that will +// only perform 10 epochs of training. (In a real application you may want to +// train for longer!) arma::fmat distance; mlpack::NCA nca; -nca.LearnDistance(dataset, labels, distance, ens::PrintLoss()); +ens::StandardSGD opt; +opt.MaxIterations() = 10 * dataset.n_cols; +nca.LearnDistance(dataset, labels, distance, opt, ens::ProgressBar()); // We want to compute six quantities: // @@ -310,21 +326,18 @@ lbfgs.MaxIterations() = 1000; arma::mat distance; mlpack::NCA nca; -// Use callbacks that print the loss at each iteration, and then print a final -// optimization report. -nca.LearnDistance(dataset, labels, distance, lbfgs, ens::PrintLoss(), - ens::Report()); +// Use a callback that prints a final optimization report. +nca.LearnDistance(dataset, labels, distance, lbfgs, ens::Report()); ``` --- -Learn a distance metric on the satellite dataset, but instead of using the -Euclidean distance as the underlying metric, use the inner-product distance of -the [`PolynomialKernel`](../core.md#polynomialkernel) with the -[`IPMetric`](../core.md#ipmetric) class. The distance metric learning is -therefore performed in kernel space. +Learn a distance metric on the vehicle dataset, but instead of using the +Euclidean distance as the underlying metric, use the Manhattan distance. This +means that NCA is optimizing k-NN performance under the Manhattan distance, not +under the Euclidean distance. ```c++ // See https://datasets.mlpack.org/vehicle.csv. @@ -347,8 +360,10 @@ dataset /= arma::max(arma::max(arma::abs(dataset))); // progress bar during optimization. mlpack::NCA nca; arma::mat distance; -nca.LearnDistance(dataset, labels, distance, ens::NesterovMomentumSGD(), - ens::ProgressBar()); +ens::NesterovMomentumSGD opt(0.01 /* step size */, + 32 /* batch size */, + 20 * dataset.n_cols /* 20 epochs */); +nca.LearnDistance(dataset, labels, distance, opt, ens::ProgressBar()); // Now inspect distances between points with the Euclidean distance and with the // inner product distance. @@ -383,3 +398,43 @@ std::cout << " * After NCA: " << d4 << std::endl; // Note that point 3 has been moved further away from point 0 than point 1. ``` + +--- + +Learn a distance metric while also performing dimensionality reduction, reducing +the dimensionality of the vehicle dataset by 2 dimensions. + +```c++ +// See https://datasets.mlpack.org/vehicle.csv. +arma::mat dataset; +mlpack::data::Load("vehicle.csv", dataset, true); + +// The labels are contained as the last row of the dataset. +arma::Row labels = + arma::conv_to>::from(dataset.row(dataset.n_rows - 1)); +dataset.shed_row(dataset.n_rows - 1); + +// Because typical distances between points in the vehicle dataset are large, +// we will center the dataset and scale it to have points in the unit ball. +// (That is, all points will have values in each dimension between -1 and 1.) +// This means that the maximum pairwise distance is 2. +dataset.each_col() -= arma::mean(dataset, 1); +dataset /= arma::max(arma::max(arma::abs(dataset))); + +// Use a random initialization for the distance transformation, with the +// specified output dimensionality. +arma::mat distance(dataset.n_rows - 2, dataset.n_rows, arma::fill::randu); +mlpack::NCA nca; +ens::L_BFGS opt; +opt.MaxIterations() = 10; // You may want more in a real application. +nca.LearnDistance(dataset, labels, distance, opt); + +// Now transform the dataset. +arma::mat transformedData = distance * dataset; + +std::cout << std::endl << std::endl; +std::cout << "Original data has size " << dataset.n_rows << " x " + << dataset.n_cols << "." << std::endl; +std::cout << "Transformed data has size " << transformedData.n_rows << " x " + << transformedData.n_cols << "." << std::endl; +``` diff --git a/src/mlpack/methods/nca/first_element_is_arma.hpp b/src/mlpack/methods/nca/first_element_is_arma.hpp new file mode 100644 index 0000000000..e82af9c152 --- /dev/null +++ b/src/mlpack/methods/nca/first_element_is_arma.hpp @@ -0,0 +1,44 @@ +/** + * @file methods/nca/first_element_is_arma.hpp + * @author Ryan Curtin + * + * Utility struct to detect whether the first element in a parameter pack is an + * Armadillo type. + */ +#ifndef MLPACK_METHODS_NCA_FIRST_ELEMENT_IS_ARMA_HPP +#define MLPACK_METHODS_NCA_FIRST_ELEMENT_IS_ARMA_HPP + +#include + +namespace mlpack { + +// This utility struct returns the first type of a parameter pack. +template +struct First +{ + typedef void type; +}; + +// This matches whenever CallbackTypes has one or more elements. +template +struct First +{ + typedef T type; +}; + +// This utility template struct detects whether the first element in a +// parameter pack is an Armadillo type. It is entirely for the deprecated +// constructor below and can be removed when that is removed during the +// release of mlpack 5.0.0. +template +struct FirstElementIsArma +{ + static constexpr bool value = arma::is_arma_type< + typename std::remove_reference< + typename First::type + >::type>::value; +}; + +} + +#endif diff --git a/src/mlpack/methods/nca/nca.hpp b/src/mlpack/methods/nca/nca.hpp index de6c63c88a..2d662e2fd3 100644 --- a/src/mlpack/methods/nca/nca.hpp +++ b/src/mlpack/methods/nca/nca.hpp @@ -15,26 +15,10 @@ #include #include "nca_softmax_error_function.hpp" +#include "first_element_is_arma.hpp" namespace mlpack { -// This utility template struct detects whether the first element in a -// parameter pack is an Armadillo type. It is entirely for the deprecated -// constructor below and can be removed when that is removed during the -// release of mlpack 5.0.0. -template -struct FirstElementIsArma -{ - static constexpr bool value = false; -}; - -template -struct FirstElementIsArma -{ - static constexpr bool value = arma::is_arma_type< - typename std::remove_reference::type>::value; -}; - /** * An implementation of Neighborhood Components Analysis, both a linear * dimensionality reduction technique and a distance learning technique. The @@ -78,6 +62,10 @@ class NCA const arma::Row& labels, DistanceType distance = DistanceType()); + /** + * Construct the Neighborhood Components Analysis object, optionally with an + * instantiated distance metric. + */ NCA(DistanceType distance = DistanceType()); /** @@ -103,9 +91,27 @@ class NCA "dataset as a parameter.")]] void LearnDistance(arma::mat& outputMatrix, CallbackTypes&&... callbacks); + /** + * Perform Neighborhood Components Analysis. The output distance learning + * matrix is written into the passed reference. If LearnDistance() is called + * with an outputMatrix which has the correct size (dataset.n_rows x + * dataset.n_rows), that matrix will be used as the starting point for + * optimization. + * + * @param dataset Dataset to learn distance metric on. + * @param labels Labels for dataset. + * @param outputMatrix Covariance matrix of Mahalanobis distance. + * @param callbacks Callback function for ensmallen optimizer `OptimizerType`. + * See https://www.ensmallen.org/docs.html#callback-documentation. + */ template::type, + SoftmaxErrorFunction, + MatType + >::value>::type, typename = typename std::enable_if::value>::type> @@ -114,6 +120,20 @@ class NCA MatType& outputMatrix, CallbackTypes&&... callbacks) const; + /** + * Perform Neighborhood Components Analysis. The output distance learning + * matrix is written into the passed reference. If LearnDistance() is called + * with an outputMatrix which has the correct size (dataset.n_rows x + * dataset.n_rows), that matrix will be used as the starting point for + * optimization. + * + * @param dataset Dataset to learn distance metric on. + * @param labels Labels for dataset. + * @param optimizer Instantiated ensmallen optimizer to use for NCA. + * @param outputMatrix Covariance matrix of Mahalanobis distance. + * @param callbacks Callback function for ensmallen optimizer `OptimizerType`. + * See https://www.ensmallen.org/docs.html#callback-documentation. + */ template template void NCA::LearnDistance( const MatType& dataset, diff --git a/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp b/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp index f0b5ca9af7..227f5ace0e 100644 --- a/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp +++ b/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp @@ -55,10 +55,8 @@ SoftmaxErrorFunction::Evaluate( const MatType& coordinates) { // Calculate the denominators and numerators, if necessary. - std::cout << "call Evaluate()\n"; Precalculate(coordinates); - std::cout << "result: " << -accu(p) << "\n"; return -accu(p); // Sum of p_i for all i. We negate because our solver // minimizes, not maximizes. }; @@ -121,7 +119,6 @@ void SoftmaxErrorFunction::Gradient( const MatType& coordinates, MatType& gradient) { // Calculate the denominators and numerators, if necessary. - std::cout << "call Gradient()\n"; Precalculate(coordinates); // Now, we handle the summation over i: @@ -274,7 +271,6 @@ void SoftmaxErrorFunction::Precalculate( // We will do this by keeping track of the denominators for each i as well as // the numerators (the sum for all j in class of i). This will be on the // order of O((n * (n + 1)) / 2), which really isn't all that great. - std::cout << "precalculating after stretch...\n"; p.zeros(stretchedDataset.n_cols); denominators.zeros(stretchedDataset.n_cols); #pragma omp parallel for collapse(2) @@ -302,7 +298,6 @@ void SoftmaxErrorFunction::Precalculate( } } } - std::cout << "now cleanup\n"; // Divide p_i by their denominators. p /= denominators; From d351a378ab944ffff111cbefbb7e2b3fb1c59ba1 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 6 Jul 2024 12:40:55 -0400 Subject: [PATCH 088/212] Templatize LMNN and add documentation. --- doc/user/methods/lmnn.md | 454 +++++++++++++++ src/mlpack/methods/lmnn/constraints.hpp | 107 ++-- src/mlpack/methods/lmnn/constraints_impl.hpp | 206 +++---- src/mlpack/methods/lmnn/lmnn.hpp | 137 ++++- src/mlpack/methods/lmnn/lmnn_function.hpp | 105 ++-- .../methods/lmnn/lmnn_function_impl.hpp | 226 ++++---- src/mlpack/methods/lmnn/lmnn_impl.hpp | 98 +++- src/mlpack/methods/lmnn/lmnn_main.cpp | 77 ++- .../neighbor_search/neighbor_search.hpp | 18 +- .../neighbor_search/neighbor_search_impl.hpp | 28 +- .../neighbor_search/neighbor_search_rules.hpp | 5 +- .../neighbor_search_rules_impl.hpp | 5 +- src/mlpack/tests/callback_test.cpp | 5 +- src/mlpack/tests/lmnn_test.cpp | 525 ++++++++++-------- src/mlpack/tests/main_tests/lmnn_test.cpp | 14 +- 15 files changed, 1363 insertions(+), 647 deletions(-) create mode 100644 doc/user/methods/lmnn.md diff --git a/doc/user/methods/lmnn.md b/doc/user/methods/lmnn.md new file mode 100644 index 0000000000..322a5f683e --- /dev/null +++ b/doc/user/methods/lmnn.md @@ -0,0 +1,454 @@ +## LMNN + +The `LMNN` class implements large margin nearest neighbor, which can be used +as both a linear dimensionality reduction technique and a distance learning +technique (also called metric learning). LMNN finds a linear transformation of +the dataset that improves `k`-nearest-neighbor classification performance. + +#### Simple usage example: + +```c++ +// Learn a distance metric that improves kNN classification performance. + +// All data and labels are uniform random; 10 dimensional data, 5 classes. +// Replace with a data::Load() call or similar for a real application. +arma::mat dataset(10, 1000, arma::fill::randu); // 1000 points. +arma::Row labels = + arma::randi>(1000, arma::distr_param(0, 4)); + +mlpack::LMNN lmnn(3 /* neighbors to consider */); // Step 1: create object. +arma::mat distance; +lmnn.LearnDistance(dataset, labels, distance); // Step 2: learn distance. + +// `distance` can now be used as a transformation matrix for the data. +arma::mat transformedData = distance * dataset; +// Or, you can create a MahalanobisDistance to evaluate points in the +// transformed dataset space. +arma::mat q = distance.t() * distance; +mlpack::MahalanobisDistance d(std::move(q)); + +std::cout << "Distance between points 0 and 1:" << std::endl; +std::cout << " - Before LMNN: " + << mlpack::EuclideanDistance::Evaluate(dataset.col(0), dataset.col(1)) + << "." << std::endl; +std::cout << " - After LMNN: " + << d.Evaluate(dataset.col(0), dataset.col(1)) << "." << std::endl; +``` +

    More examples...

    + +#### Quick links: + + * [Constructors](#constructors): create `LMNN` objects. + * [`LearnDistance()`](#learning-distances): learn distance metrics. + * [Other functionality](#other-functionality) for loading and saving. + * [Examples](#simple-examples) of simple usage and integration with other + techniques. + +#### See also: + + + + * [mlpack distance metrics](../core.md#distances) + * [`NCA`](nca.md) + * [Metric learning on Wikipedia](https://en.wikipedia.org/wiki/Similarity_learning#Metric_learning) + * [Large margin nearest neighbor on Wikipedia](https://en.wikipedia.org/wiki/Large_margin_nearest_neighbor) + * [Distance metric learning for Large Margin Nearest Neighbor Classification (pdf)](https://proceedings.neurips.cc/paper_files/paper/2005/file/a7f592cef8b130a6967a90617db5681b-Paper.pdf) + +### Constructors + + * `lmnn = LMNN(k, regularization=0.5, updateInterval=1)` + - Create an `LMNN` object considering the specified number `k` of neighbors. + - Optionally, specify the regularization to be applied to the LMNN cost + function (a `double`), and the number of iterations between recomputation + of neighbors (`updateInterval`, a `size_t`). + +--- + + * `lmnn = LMNN(k, regularization=0.5, updateInterval=1)` + * `lmnn = LMNN(k, regularization, updateInterval, distance)` + - Create an `LMNN` object using a custom + [`DistanceType`](../core.md#distances). + - `k` specifies the number of neighbors to consider. + - `regularization` specifies the regularization penalty to be applied to the + LMNN cost function (a `double`). + - `updateInterval` specifies the number of iterations between recomputation + of neighbors (a `size_t`). + - An instantiated `DistanceType` can optionally be passed with the `distance` + parameter. + - Using a custom `DistanceType` means that `LearnDistance()` will learn a + linear transformation for the data *in the metric space of the custom + `DistanceType`*. + * This means any learned distance may not necessarily improve + classification performance with the + [Euclidean distance](../core.md#lmetric). + * Instead, classification performance will be improved when the learned + distance is used with the given `DistanceType` only. + - Any mlpack `DistanceType` can be used as a drop-in replacement, or a + [custom `DistanceType`](../../developer/distances.md). + * A list of mlpack's provided distance metrics can be found + [here](../core.md#distances). + - ***Note: be sure that you understand the implications of a custom + `DistanceType` before using this version.*** + +--- + +***Notes***: + + - A larger `k` will cause `LearnDistance()` to take longer to compute, but will + give more accurate results. It is generally suggested to keep `k` in roughly + the `3` to `5` range, depending on the dataset. Using `k = 1` can provide + fast convergence, but the learned distance metric may be of lower quality. + + - `regularization` controls the balance between encouraging small distances for + points of the same class and penalizing small distances for points of + different classes. When `regularization` is increased, small distances for + points of different classes are further penalized. + + - Setting `updateInterval` greater than `1` will allow the LMNN algorithm to + take multiple steps without the expensive recomputation of neighbors, but + this means that subsequent optimization steps may not be using the true + nearest neighbors. + * If using an SGD-like algorithm (i.e. an optimizer for a + [differentiable separable function](https://www.ensmallen.org/docs.html#differentiable-separable-functions)), + this can often be set to a relatively high value (100 is not unreasonable). + * If using an optimizer like L-BFGS (i.e. a full-batch optimizer for + [differentiable functions](https://www.ensmallen.org/docs.html#differentiable-functions)), + this should be kept relatively low (going above 10 is not advised). + * It is worth cross-validating different values of the parameter to see what + works for your dataset. + +--- + +### Learning Distances + +Once an `LMNN` object has been created, the `LearnDistance()` method can be used +to learn a distance. + + * `lmnn.LearnDistance(data, labels, distance, [callbacks...])` + * `lmnn.LearnDistance(data, labels, distance, optimizer, [callbacks...])` + - Learn a distance metric on the given `data` and `labels`, filling + `distance` with a transformation matrix that can be used to map the data + into the space of the learned distance. + - Optionally, pass an instantiated + [ensmallen optimizer](https://www.ensmallen.org) and/or + [ensmallen callbacks](https://www.ensmallen.org/docs.html#callback-documentation) + to be used for the learning process. + - If no optimizer is passed, + [`ens::AMSGrad`](https://www.ensmallen.org/docs.html#amsgrad) is used. + - If `distance` already has size `r` x `data.n_rows` for some `r` less than + or equal to `data.n_rows`, it will be used as the starting point for + optimization. Otherwise, the identity matrix with size `data.n_rows` x + `data.n_rows` will be used. + - When optimization is complete, `distance` will have size `r` x + `data.n_rows`, where `r` is less than or equal to `data.n_rows`. + * *Note*: If `r < data.n_rows`, then LMNN has learned a distance metric + that also reduces the dimensionality of the data. See the + [last example](#simple-examples). + +To use `distance`, either: + + * Compute a new transformed dataset as `distance * data`, or + * Use an instantiated [`MahalanobisDistance`](../core.md#mahalanobisdistance) + with `distance.t() * distance` as the `Q` matrix. + +See the [examples section](#simple-examples) for more details. + +#### `LearnDistance()` Parameters: + +| **name** | **type** | **description** | +|----------|----------|-----------------| +| `data` | [`arma::mat`](../matrices.md) | [Column-major](../matrices.md#representing-data-in-mlpack) training matrix. | +| `labels` | [`arma::Row`](../matrices.md) | Training labels, [between `0` and `numClasses - 1`](../load_save.md#normalizing-labels) (inclusive). Should have length `data.n_cols`. | +| `distance` | [`arma::mat`](../matrices.md) | Output matrix to store transformation matrix representing learned distance. | +| `optimizer` | [any ensmallen optimizer](https://www.ensmallen.org) | Instantiated ensmallen optimizer for [differentiable functions](https://www.ensmallen.org/docs.html#differentiable-functions) or [differentiable separable functions](https://www.ensmallen.org/docs.html#differentiable-separable-functions). | `ens::AMSGrad()` | +| `callbacks...` | [any set of ensmallen callbacks](https://www.ensmallen.org/docs.html#callback-documentation) | Optional callbacks for the ensmallen optimizer, such as e.g. `ens::ProgressBar()`, `ens::Report()`, or others. | _(N/A)_ | + +***Note***: any matrix type can be used for `data` and `distance`, so long as +that type implements the Armadillo API. So, e.g., `arma::fmat` can be used. + +### Other Functionality + + * An `LMNN` object can be serialized with + [`data::Save()` and `data::Load()`](../load_save.md#mlpack-objects). + Note that this is only meaningful if a custom `DistanceType` is being used, + and that custom `DistanceType` has state to be saved. + + * `lmnn.K()` returns the number of neighbors used by LMNN, and `lmnn.K() = k` + will set the number of neighbors to use to `k`. + + * `lmnn.Regularization()` returns the current regularization value of the LMNN + object (as a `double`), and `lmnn.Regularization() = r` can be used to set + the regularization value to `r`. + + * `lmnn.UpdateInterval()` returns the current number of iterations between + neighbor recomputation (as a `size_t`), and `lmnn.UpdateInterval() = i` sets + the number of iterations between neighbor recomputation to `i`. + + * `lmnn.Distance()` will return the `DistanceType` being used for learning. + Unless a custom `DistanceType` was specified in the constructor, + this simply returns a [`SquaredEuclideanDistance`](../core.md#lmetric) + object. + +### Simple Examples + +Learn a distance metric to improve classification performance on the iris +dataset, and show improved performance when using +[`NaiveBayesClassifier`](naive_bayes_classifier.md). + +```c++ +// See https://datasets.mlpack.org/satellite.test.csv. +// (We are using the test set here just because it is a little smaller and +// we want this example to run quickly.) +arma::mat dataset; +mlpack::data::Load("satellite.test.csv", dataset, true); +// See https://datasets.mlpack.org/satellite.test.labels.csv. +arma::Row labels; +mlpack::data::Load("satellite.test.labels.csv", labels, true); + +// Create an LMNN object using 5 nearest neighbors and learn a distance. +arma::mat distance; +mlpack::LMNN lmnn(5); +lmnn.LearnDistance(dataset, labels, distance); + +// The distance matrix has size equal to the dimensionality of the data. +std::cout << "Learned distance size: " << distance.n_rows << " x " + << distance.n_cols << "." << std::endl; + +// Learn a NaiveBayesClassifier model on the data and print the performance. +mlpack::NaiveBayesClassifier nbc1(dataset, labels, 2); +arma::Row predictions; +nbc1.Classify(dataset, predictions); +std::cout << "Naive Bayes Classifier without LMNN: " + << arma::accu(labels == predictions) << " of " << labels.n_elem + << " correct." << std::endl; + +// Now transform the data and learn another NaiveBayesClassifier. +arma::mat transformedDataset = distance * dataset; +mlpack::NaiveBayesClassifier nbc2(transformedDataset, labels, 2); +nbc2.Classify(transformedDataset, predictions); +std::cout << "Naive Bayes Classifier with LMNN: " + << arma::accu(labels == predictions) << " of " << labels.n_elem + << " correct." << std::endl; +``` + +--- + +Learn a distance metric on the vehicle dataset, using 32-bit floating point to +represent the data and metric. + +```c++ +// See https://datasets.mlpack.org/vehicle.csv. +arma::fmat dataset; +mlpack::data::Load("vehicle.csv", dataset, true); + +// The labels are contained as the last row of the dataset. +arma::Row labels = + arma::conv_to>::from(dataset.row(dataset.n_rows - 1)); +dataset.shed_row(dataset.n_rows - 1); + +// Create an LMNN object with k=1 and learn distance on float32 data. +// Set updateInterval to a large value (100) because we are using the default +// AMSGrad optimizer (which will take very many small steps). +arma::fmat distance; +mlpack::LMNN lmnn(1, 0.5, 100); + +lmnn.LearnDistance(dataset, labels, distance, ens::ProgressBar()); + +// We want to compute six quantities: +// +// - Average distance to points of the same class before LMNN. +// - Average distance to points of the same class after LMNN, using +// MahalanobisDistance. +// - Average distance to points of the same class after LMNN, using the +// transformed dataset. +// +// - The same three quantities above, but for points of the other class. +// +// LMNN should reduce the average distance to points in the same class, while +// increasing the average distance to points in other classes. +float distSums[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; +size_t sameCount = 0; +arma::fmat q = distance.t() * distance; +mlpack::MahalanobisDistance md(std::move(q)); +arma::fmat transformedDataset = distance * dataset; +for (size_t i = 1; i < dataset.n_cols; ++i) +{ + const double d1 = mlpack::EuclideanDistance::Evaluate( + dataset.col(0), dataset.col(i)); + const double d2 = md.Evaluate(dataset.col(0), dataset.col(i)); + const double d3 = mlpack::EuclideanDistance::Evaluate( + transformedDataset.col(0), transformedDataset.col(i)); + + // Determine whether the point has the same label as point 0. + if (labels[i] == labels[0]) + { + distSums[0] += d1; + distSums[1] += d2; + distSums[2] += d3; + ++sameCount; + } + else + { + distSums[3] += d1; + distSums[4] += d2; + distSums[5] += d3; + } +} + +// Turn the results into average distances across the class. +distSums[0] /= sameCount; +distSums[1] /= sameCount; +distSums[2] /= sameCount; +distSums[3] /= (dataset.n_cols - sameCount); +distSums[4] /= (dataset.n_cols - sameCount); +distSums[5] /= (dataset.n_cols - sameCount); + +// Print the results. +std::cout << "Average distance between point 0 and other points of the same " + << "class:" << std::endl; +std::cout << " - Before LMNN: " << distSums[0] << "." + << std::endl; +std::cout << " - After LMNN (with MahalanobisDistance): " << distSums[1] << "." + << std::endl; +std::cout << " - After LMNN (with transformed dataset): " << distSums[2] << "." + << std::endl; +std::cout << std::endl; + +std::cout << "Average distance between point 0 and points of other classes: " + << std::endl; +std::cout << " - Before LMNN: " << distSums[3] << "." + << std::endl; +std::cout << " - After LMNN (with MahalanobisDistance): " << distSums[4] << "." + << std::endl; +std::cout << " - After LMNN (with transformed dataset): " << distSums[5] << "." + << std::endl; +std::cout << std::endl; + +std::cout << "Ratio of other-class to same-class distances:" << std::endl; +std::cout << "(We expect this to go up.)" << std::endl; +std::cout << " - Before LMNN: " << (distSums[3] / distSums[0]) << "." + << std::endl; +std::cout << " - After LMNN: " << (distSums[5] / distSums[2]) << "." + << std::endl; +``` + +--- + +Learn a distance metric on the iris dataset, using the L-BFGS optimizer with +callbacks. + +```c++ +// See https://datasets.mlpack.org/iris.csv. +arma::mat dataset; +mlpack::data::Load("iris.csv", dataset, true); +// See https://datasets.mlpack.org/iris.labels.csv. +arma::Row labels; +mlpack::data::Load("iris.labels.csv", labels, true); + +// Learn a distance with ensmallen's L-BFGS optimizer. +ens::L_BFGS lbfgs; +lbfgs.NumBasis() = 5; +lbfgs.MaxIterations() = 1000; + +// Use 5 neighbors for LMNN, and leave updateInterval at the default of 1, +// because we are using L-BFGS (a full-back optimizer). +mlpack::LMNN lmnn(5); + +// Use a callback that prints a final optimization report. +arma::mat distance; +lmnn.LearnDistance(dataset, labels, distance, lbfgs, ens::Report()); +``` + +--- + +Learn a distance metric on the vehicle dataset, but instead of using the +Euclidean distance as the underlying metric, use the Manhattan distance. This +means that LMNN is optimizing k-NN performance under the Manhattan distance, not +under the Euclidean distance. + +```c++ +// See https://datasets.mlpack.org/vehicle.csv. +arma::mat dataset; +mlpack::data::Load("vehicle.csv", dataset, true); + +// The labels are contained as the last row of the dataset. +arma::Row labels = + arma::conv_to>::from(dataset.row(dataset.n_rows - 1)); +dataset.shed_row(dataset.n_rows - 1); + +// Create the LMNN object and optimize. Use k=3 and Nesterov momentum SGD, +// printing a progress bar during optimization. Because Nesterov momentum SGD +// is an ensmallen optimizer for differentiable separable functions, we increase +// updateInterval to reduce the number of neighbor recomputations. We also set +// the regularization parameter to 1.0 to increase the penalty for nearby +// neighbors of a different class. +mlpack::LMNN lmnn(3, 1.0, 100); +arma::mat distance; +ens::NesterovMomentumSGD opt(0.000001 /* step size */, + 32 /* batch size */, + 20 * dataset.n_cols /* 20 epochs */); +lmnn.LearnDistance(dataset, labels, distance, opt, ens::ProgressBar()); + +// Now inspect distances between points with the Euclidean distance and with the +// inner product distance. +arma::mat transformedDataset = distance * dataset; + +// Points 0 and 1 have the same label (0). See their original distance---with +// both the Euclidean and Manhattan distances---and their transformed distances. +// We expect these points to get closer together, in the Manhattan distance. +const double d1 = mlpack::ManhattanDistance::Evaluate( + dataset.col(0), dataset.col(1)); +const double d2 = mlpack::ManhattanDistance::Evaluate( + transformedDataset.col(0), transformedDataset.col(1)); + +std::cout << "Distance between points 0 and 1 (same class):" << std::endl; +std::cout << " - Manhattan distance:" << std::endl; +std::cout << " * Before LMNN: " << d1 << std::endl; +std::cout << " * After LMNN: " << d2 << std::endl; +std::cout << std::endl; + +// Point 3 has a different label. We therefore expect this point to get further +// from point 0 with the Manhattan distance, but not necessarily with the +// Euclidean distance. +const double d3 = mlpack::ManhattanDistance::Evaluate( + dataset.col(0), dataset.col(3)); +const double d4 = mlpack::ManhattanDistance::Evaluate( + transformedDataset.col(0), transformedDataset.col(3)); + +std::cout << "Distance between points 0 and 3 (different class):" << std::endl; +std::cout << " - Manhattan distance:" << std::endl; +std::cout << " * Before LMNN: " << d3 << std::endl; +std::cout << " * After LMNN: " << d4 << std::endl; + +// Note that point 3 has been moved further away from point 0 than point 1. +``` + +--- + +Learn a distance metric while also performing dimensionality reduction, reducing +the dimensionality of the satellite dataset by 3 dimensions. + +```c++ +// See https://datasets.mlpack.org/satellite.train.csv. +arma::mat dataset; +mlpack::data::Load("satellite.train.csv", dataset, true); +// See https://datasets.mlpack.org/satellite.labels.csv. +arma::Row labels; +mlpack::data::Load("satellite.train.labels.csv", labels, true); + +// Use a random initialization for the distance transformation, with the +// specified output dimensionality. +arma::mat distance(dataset.n_rows - 3, dataset.n_rows, arma::fill::randu); +mlpack::LMNN lmnn(3); +ens::L_BFGS opt; +opt.MaxIterations() = 10; // You may want more in a real application. +lmnn.LearnDistance(dataset, labels, distance, opt, ens::Report()); + +// Now transform the dataset. +arma::mat transformedData = distance * dataset; + +std::cout << "Original data has size " << dataset.n_rows << " x " + << dataset.n_cols << "." << std::endl; +std::cout << "Transformed data has size " << transformedData.n_rows << " x " + << transformedData.n_cols << "." << std::endl; +``` diff --git a/src/mlpack/methods/lmnn/constraints.hpp b/src/mlpack/methods/lmnn/constraints.hpp index 082b8961ed..7704d239fe 100644 --- a/src/mlpack/methods/lmnn/constraints.hpp +++ b/src/mlpack/methods/lmnn/constraints.hpp @@ -27,12 +27,25 @@ namespace mlpack { * data point) and Triplets() (Generates sets of {dataset, target neighbors, * impostors} tripltets.) */ -template +template, + typename DistanceType = SquaredEuclideanDistance> class Constraints { public: //! Convenience typedef. - typedef NeighborSearch KNN; + typedef NeighborSearch KNN; + + // Convenience typedef for element type of data. + typedef typename MatType::elem_type ElemType; + // Convenience typedef for column vector of data. + typedef typename GetColType::type VecType; + // Convenience typedef for cube of data. + typedef typename GetCubeType::type CubeType; + // Convenience typedef for dense matrix of indices. + typedef typename GetUDenseMatType::type UMatType; + // Convenience typedef for dense vector of indices. + typedef typename GetColType::type UVecType; /** * Constructor for creating a Constraints instance. @@ -41,8 +54,8 @@ class Constraints * @param labels Input dataset labels. * @param k Number of target neighbors, impostors & triplets. */ - Constraints(const arma::mat& dataset, - const arma::Row& labels, + Constraints(const MatType& dataset, + const LabelsType& labels, const size_t k); /** @@ -54,10 +67,10 @@ class Constraints * @param labels Input dataset labels. * @param norms Input dataset norms. */ - void TargetNeighbors(arma::Mat& outputMatrix, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms); + void TargetNeighbors(UMatType& outputMatrix, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms); /** * Calculates k similar labeled nearest neighbors for a batch of dataset and @@ -70,10 +83,10 @@ class Constraints * @param begin Index of the initial point of dataset. * @param batchSize Number of data points to use. */ - void TargetNeighbors(arma::Mat& outputMatrix, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms, + void TargetNeighbors(UMatType& outputMatrix, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms, const size_t begin, const size_t batchSize); @@ -86,10 +99,10 @@ class Constraints * @param labels Input dataset labels. * @param norms Input dataset norms. */ - void Impostors(arma::Mat& outputMatrix, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms); + void Impostors(UMatType& outputMatrix, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms); /** * Calculates k differently labeled nearest neighbors & distances to @@ -101,11 +114,11 @@ class Constraints * @param labels Input dataset labels. * @param norms Input dataset norms. */ - void Impostors(arma::Mat& outputNeighbors, - arma::mat& outputDistance, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms); + void Impostors(UMatType& outputNeighbors, + MatType& outputDistance, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms); /** * Calculates k differently labeled nearest neighbors for a batch of dataset @@ -118,10 +131,10 @@ class Constraints * @param begin Index of the initial point of dataset. * @param batchSize Number of data points to use. */ - void Impostors(arma::Mat& outputMatrix, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms, + void Impostors(UMatType& outputMatrix, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms, const size_t begin, const size_t batchSize); @@ -137,11 +150,11 @@ class Constraints * @param begin Index of the initial point of dataset. * @param batchSize Number of data points to use. */ - void Impostors(arma::Mat& outputNeighbors, - arma::mat& outputDistance, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms, + void Impostors(UMatType& outputNeighbors, + MatType& outputDistance, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms, const size_t begin, const size_t batchSize); @@ -158,12 +171,12 @@ class Constraints * @param points Indices of data points to calculate impostors on. * @param numPoints Number of points to actually calculate impostors on. */ - void Impostors(arma::Mat& outputNeighbors, - arma::mat& outputDistance, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms, - const arma::uvec& points, + void Impostors(UMatType& outputNeighbors, + MatType& outputDistance, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms, + const UVecType& points, const size_t numPoints); /** @@ -175,10 +188,10 @@ class Constraints * @param labels Input dataset labels. * @param norms Input dataset norms. */ - void Triplets(arma::Mat& outputMatrix, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms); + void Triplets(UMatType& outputMatrix, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms); //! Get the number of target neighbors (k). const size_t& K() const { return k; } @@ -195,13 +208,13 @@ class Constraints size_t k; //! Store unique labels. - arma::Row uniqueLabels; + LabelsType uniqueLabels; //! Store indices of data points having similar label. - std::vector indexSame; + std::vector indexSame; //! Store indices of data points having different label. - std::vector indexDiff; + std::vector indexDiff; //! False if nothing has ever been precalculated. bool precalculated; @@ -210,15 +223,15 @@ class Constraints * Precalculate the unique labels, and indices of similar * and different datapoints on the basis of labels. */ - inline void Precalculate(const arma::Row& labels); + inline void Precalculate(const LabelsType& labels); /** * Re-order neighbors on the basis of increasing norm in case * of ties among distances. */ - inline void ReorderResults(const arma::mat& distances, - arma::Mat& neighbors, - const arma::vec& norms); + inline void ReorderResults(const MatType& distances, + UMatType& neighbors, + const VecType& norms); }; } // namespace mlpack diff --git a/src/mlpack/methods/lmnn/constraints_impl.hpp b/src/mlpack/methods/lmnn/constraints_impl.hpp index 6f227fd970..27ed28417a 100644 --- a/src/mlpack/methods/lmnn/constraints_impl.hpp +++ b/src/mlpack/methods/lmnn/constraints_impl.hpp @@ -17,10 +17,10 @@ namespace mlpack { -template -Constraints::Constraints( - const arma::mat& /* dataset */, - const arma::Row& labels, +template +Constraints::Constraints( + const MatType& /* dataset */, + const LabelsType& labels, const size_t k) : k(k), precalculated(false) @@ -36,11 +36,11 @@ Constraints::Constraints( } } -template -inline void Constraints::ReorderResults( - const arma::mat& distances, - arma::Mat& neighbors, - const arma::vec& norms) +template +inline void Constraints::ReorderResults( + const MatType& distances, + UMatType& neighbors, + const VecType& norms) { // Shortcut... if (neighbors.n_rows == 1) @@ -64,24 +64,21 @@ inline void Constraints::ReorderResults( if (start != end) { // We must sort these elements by norm. - arma::Col newNeighbors = - neighbors.col(i).subvec(start, end - 1); - arma::uvec indices = ConvTo::From(newNeighbors); - - arma::uvec order = arma::sort_index(norms.elem(indices)); - neighbors.col(i).subvec(start, end - 1) = - newNeighbors.elem(order); + UVecType indices = neighbors.col(i).subvec(start, end - 1); + UVecType order = arma::sort_index(norms.elem(indices)); + neighbors.col(i).subvec(start, end - 1) = indices.elem(order); } } } } // Calculates k similar labeled nearest neighbors. -template -void Constraints::TargetNeighbors(arma::Mat& outputMatrix, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms) +template +void Constraints::TargetNeighbors( + UMatType& outputMatrix, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms) { // Perform pre-calculation. If neccesary. Precalculate(labels); @@ -89,8 +86,8 @@ void Constraints::TargetNeighbors(arma::Mat& outputMatrix, // KNN instance. KNN knn; - arma::Mat neighbors; - arma::mat distances; + UMatType neighbors; + MatType distances; for (size_t i = 0; i < uniqueLabels.n_cols; ++i) { @@ -114,28 +111,29 @@ void Constraints::TargetNeighbors(arma::Mat& outputMatrix, // Calculates k similar labeled nearest neighbors on a // batch of data points. -template -void Constraints::TargetNeighbors(arma::Mat& outputMatrix, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms, - const size_t begin, - const size_t batchSize) +template +void Constraints::TargetNeighbors( + UMatType& outputMatrix, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms, + const size_t begin, + const size_t batchSize) { // Perform pre-calculation. If neccesary. Precalculate(labels); - arma::mat subDataset = dataset.cols(begin, begin + batchSize - 1); - arma::Row sublabels = labels.cols(begin, begin + batchSize - 1); + MatType subDataset = dataset.cols(begin, begin + batchSize - 1); + LabelsType sublabels = labels.cols(begin, begin + batchSize - 1); // KNN instance. KNN knn; - arma::Mat neighbors; - arma::mat distances; + UMatType neighbors; + MatType distances; // Vectors to store indices. - arma::uvec subIndexSame; + UVecType subIndexSame; for (size_t i = 0; i < uniqueLabels.n_cols; ++i) { @@ -161,11 +159,12 @@ void Constraints::TargetNeighbors(arma::Mat& outputMatrix, } // Calculates k differently labeled nearest neighbors. -template -void Constraints::Impostors(arma::Mat& outputMatrix, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms) +template +void Constraints::Impostors( + UMatType& outputMatrix, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms) { // Perform pre-calculation. If neccesary. Precalculate(labels); @@ -173,8 +172,8 @@ void Constraints::Impostors(arma::Mat& outputMatrix, // KNN instance. KNN knn; - arma::Mat neighbors; - arma::mat distances; + UMatType neighbors; + MatType distances; for (size_t i = 0; i < uniqueLabels.n_cols; ++i) { @@ -198,12 +197,13 @@ void Constraints::Impostors(arma::Mat& outputMatrix, // Calculates k differently labeled nearest neighbors. The function // writes back calculated neighbors & distances to passed matrices. -template -void Constraints::Impostors(arma::Mat& outputNeighbors, - arma::mat& outputDistance, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms) +template +void Constraints::Impostors( + UMatType& outputNeighbors, + MatType& outputDistance, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms) { // Perform pre-calculation. If neccesary. Precalculate(labels); @@ -211,8 +211,8 @@ void Constraints::Impostors(arma::Mat& outputNeighbors, // KNN instance. KNN knn; - arma::Mat neighbors; - arma::mat distances; + UMatType neighbors; + MatType distances; for (size_t i = 0; i < uniqueLabels.n_cols; ++i) { @@ -237,28 +237,29 @@ void Constraints::Impostors(arma::Mat& outputNeighbors, // Calculates k differently labeled nearest neighbors on a // batch of data points. -template -void Constraints::Impostors(arma::Mat& outputMatrix, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms, - const size_t begin, - const size_t batchSize) +template +void Constraints::Impostors( + UMatType& outputMatrix, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms, + const size_t begin, + const size_t batchSize) { // Perform pre-calculation. If neccesary. Precalculate(labels); - arma::mat subDataset = dataset.cols(begin, begin + batchSize - 1); - arma::Row sublabels = labels.cols(begin, begin + batchSize - 1); + MatType subDataset = dataset.cols(begin, begin + batchSize - 1); + LabelsType sublabels = labels.cols(begin, begin + batchSize - 1); // KNN instance. KNN knn; - arma::Mat neighbors; - arma::mat distances; + UMatType neighbors; + MatType distances; // Vectors to store indices. - arma::uvec subIndexSame; + UVecType subIndexSame; for (size_t i = 0; i < uniqueLabels.n_cols; ++i) { @@ -285,29 +286,30 @@ void Constraints::Impostors(arma::Mat& outputMatrix, // Calculates k differently labeled nearest neighbors & distances on a // batch of data points. -template -void Constraints::Impostors(arma::Mat& outputNeighbors, - arma::mat& outputDistance, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms, - const size_t begin, - const size_t batchSize) +template +void Constraints::Impostors( + UMatType& outputNeighbors, + MatType& outputDistance, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms, + const size_t begin, + const size_t batchSize) { // Perform pre-calculation. If neccesary. Precalculate(labels); - arma::mat subDataset = dataset.cols(begin, begin + batchSize - 1); - arma::Row sublabels = labels.cols(begin, begin + batchSize - 1); + MatType subDataset = dataset.cols(begin, begin + batchSize - 1); + LabelsType sublabels = labels.cols(begin, begin + batchSize - 1); // KNN instance. KNN knn; - arma::Mat neighbors; - arma::mat distances; + UMatType neighbors; + MatType distances; // Vectors to store indices. - arma::uvec subIndexSame; + UVecType subIndexSame; for (size_t i = 0; i < uniqueLabels.n_cols; ++i) { @@ -335,14 +337,15 @@ void Constraints::Impostors(arma::Mat& outputNeighbors, // Calculates k differently labeled nearest neighbors & distances over some // data points. -template -void Constraints::Impostors(arma::Mat& outputNeighbors, - arma::mat& outputDistance, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms, - const arma::uvec& points, - const size_t numPoints) +template +void Constraints::Impostors( + UMatType& outputNeighbors, + MatType& outputDistance, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms, + const UVecType& points, + const size_t numPoints) { // Perform pre-calculation. If neccesary. Precalculate(labels); @@ -350,11 +353,11 @@ void Constraints::Impostors(arma::Mat& outputNeighbors, // KNN instance. KNN knn; - arma::Mat neighbors; - arma::mat distances; + UMatType neighbors; + MatType distances; // Vectors to store indices. - arma::uvec subIndexSame; + UVecType subIndexSame; for (size_t i = 0; i < uniqueLabels.n_cols; ++i) { @@ -384,31 +387,35 @@ void Constraints::Impostors(arma::Mat& outputNeighbors, // Generates {data point, target neighbors, impostors} triplets using // TargetNeighbors() and Impostors(). -template -void Constraints::Triplets(arma::Mat& outputMatrix, - const arma::mat& dataset, - const arma::Row& labels, - const arma::vec& norms) +template +void Constraints::Triplets( + UMatType& outputMatrix, + const MatType& dataset, + const LabelsType& labels, + const VecType& norms) { // Perform pre-calculation. If neccesary. Precalculate(labels); size_t N = dataset.n_cols; - arma::Mat impostors(k, dataset.n_cols); + UMatType impostors(k, dataset.n_cols); Impostors(impostors, dataset, labels, norms); - arma::Mat targetNeighbors(k, dataset.n_cols);; + UMatType targetNeighbors(k, dataset.n_cols);; TargetNeighbors(targetNeighbors, dataset, labels, norms); - outputMatrix = arma::Mat(3, k * k * N , arma::fill::zeros); + outputMatrix = UMatType(3, k * k * N , arma::fill::zeros); - for (size_t i = 0, r = 0; i < N; ++i) + #pragma omp parallel for collapse(3) + for (size_t i = 0; i < N; ++i) { for (size_t j = 0; j < k; ++j) { - for (size_t l = 0; l < k; l++, r++) + for (size_t l = 0; l < k; l++) { + const size_t r = i * (k * k) + j * k + l; + // Generate triplets. outputMatrix(0, r) = i; outputMatrix(1, r) = targetNeighbors(j, i); @@ -418,9 +425,9 @@ void Constraints::Triplets(arma::Mat& outputMatrix, } } -template -inline void Constraints::Precalculate( - const arma::Row& labels) +template +inline void Constraints::Precalculate( + const LabelsType& labels) { // Make sure the calculation is necessary. if (precalculated) @@ -431,6 +438,7 @@ inline void Constraints::Precalculate( indexSame.resize(uniqueLabels.n_elem); indexDiff.resize(uniqueLabels.n_elem); + #pragma omp parallel for for (size_t i = 0; i < uniqueLabels.n_elem; ++i) { // Store same and diff indices. diff --git a/src/mlpack/methods/lmnn/lmnn.hpp b/src/mlpack/methods/lmnn/lmnn.hpp index 92646df11a..786cbcf924 100644 --- a/src/mlpack/methods/lmnn/lmnn.hpp +++ b/src/mlpack/methods/lmnn/lmnn.hpp @@ -14,6 +14,7 @@ #include +#include "../nca/first_element_is_arma.hpp" #include "constraints.hpp" #include "lmnn_function.hpp" @@ -49,7 +50,7 @@ namespace mlpack { * @tparam OptimizerType Optimizer to use for developing distance. */ template + typename DeprecatedOptimizerType = ens::AMSGrad> class LMNN { public: @@ -63,11 +64,27 @@ class LMNN * @param k Number of targets to consider. * @param distance Type of distance metric used for computation. */ + [[deprecated("Will be removed in mlpack 5.0.0. Pass the dataset directly to " + "LearnDistance() instead.")]] LMNN(const arma::mat& dataset, const arma::Row& labels, const size_t k, const DistanceType distance = DistanceType()); + /** + * Construct the LMNN object, optionally with an instantiated distance metric. + * + * @param k Number of target neighbors to consider. + * @param regularization Penalty to apply to objective function. + * @param updateInterval Number of iterations between each recomputation of + * true neighbors and impostors. + * @param distance Instantiated distance metric for computation. + */ + LMNN(const size_t k, + const double regularization = 0.5, + const size_t updateInterval = 1, + DistanceType distance = DistanceType()); + /** * Perform Large Margin Nearest Neighbors metric learning. The output @@ -80,25 +97,99 @@ class LMNN * @param callbacks Callback function for ensmallen optimizer `OptimizerType`. * See https://www.ensmallen.org/docs.html#callback-documentation. */ - template + template::value>::type, + typename = typename std::enable_if< + !FirstElementIsArma::value + >::type> + [[deprecated("Will be removed in mlpack 5.0.0. Use the version that takes a " + "dataset as a parameter.")]] void LearnDistance(arma::mat& outputMatrix, CallbackTypes&&... callbacks); + /** + * Perform Large Margin Nearest Neighbors metric learning. The output + * distance matrix is written into the passed reference. If the + * LearnDistance() is called with an outputMatrix with correct dimensions, + * then that matrix will be used as the starting point for optimization. + * + * @param dataset Dataset to learn distance metric on. + * @param labels Labels for dataset. + * @param outputMatrix Covariance matrix of Mahalanobis distance. + * @param callbacks Callback function for ensmallen optimizer `OptimizerType`. + * See https://www.ensmallen.org/docs.html#callback-documentation. + */ + template::type, + LMNNFunction, + MatType + >::value>::type, + typename = typename std::enable_if::value>::type> + void LearnDistance(const MatType& dataset, + const LabelsType& labels, + MatType& outputMatrix, + CallbackTypes&&... callbacks) const; + + /** + * Perform Large Margin Nearest Neighbors metric learning. The output + * distance matrix is written into the passed reference. If the + * LearnDistance() is called with an outputMatrix with correct dimensions, + * then that matrix will be used as the starting point for optimization. + * + * @param dataset Dataset to learn distance metric on. + * @param labels Labels for dataset. + * @param optimizer Instantiated ensmallen optimizer to use for LMNN. + * @param outputMatrix Covariance matrix of Mahalanobis distance. + * @param callbacks Callback function for ensmallen optimizer `OptimizerType`. + * See https://www.ensmallen.org/docs.html#callback-documentation. + */ + template, + MatType + >::value>::type> + void LearnDistance(const MatType& dataset, + const LabelsType& labels, + MatType& outputMatrix, + OptimizerType& optimizer, + CallbackTypes&&... callbacks) const; //! Get the dataset reference. - const arma::mat& Dataset() const { return dataset; } + [[deprecated("Will be removed in mlpack 5.0.0. Use the LearnDistance() " + "version that takes the optimizer as a parameter instead.")]] + const arma::mat& Dataset() const { return *dataset; } //! Get the labels reference. - const arma::Row& Labels() const { return labels; } + [[deprecated("Will be removed in mlpack 5.0.0. Use the LearnDistance() " + "version that takes the optimizer as a parameter instead.")]] + const arma::Row& Labels() const { return *labels; } //! Access the regularization value. const double& Regularization() const { return regularization; } //! Modify the regularization value. double& Regularization() { return regularization; } - //! Access the range value. - const size_t& Range() const { return range; } - //! Modify the range value. - size_t& Range() { return range; } + //! Access the iteration update interval value. + const size_t& UpdateInterval() const { return updateInterval; } + //! Modify the iteration update interval value. + size_t& UpdateInterval() { return updateInterval; } + + [[deprecated("Will be removed in mlpack 5.0.0. Use UpdateInterval() " + "instead.")]] + const size_t& Range() const { return updateInterval; } + [[deprecated("Will be removed in mlpack 5.0.0. Use UpdateInterval() " + "instead.")]] + size_t& Range() { return updateInterval; } //! Access the value of k. const size_t& K() const { return k; } @@ -106,15 +197,23 @@ class LMNN size_t K() { return k; } //! Get the optimizer. - const OptimizerType& Optimizer() const { return optimizer; } - OptimizerType& Optimizer() { return optimizer; } + [[deprecated("Will be removed in mlpack 5.0.0. Use the LearnDistance() " + "version that takes the optimizer as a parameter instead.")]] + const DeprecatedOptimizerType& Optimizer() const { return optimizer; } + //! Modify the optimizer. + [[deprecated("Will be removed in mlpack 5.0.0. Use the LearnDistance() " + "version that takes the optimizer as a parameter instead.")]] + DeprecatedOptimizerType& Optimizer() { return optimizer; } + + // Serialize the LMNN object. + template + void serialize(Archive& ar, const unsigned int /* version */); private: - //! Dataset reference. - const arma::mat& dataset; - - //! Labels reference. - const arma::Row& labels; + //! Dataset pointer (will be removed in mlpack 5.0.0). + const arma::mat* dataset; + //! Labels pointer (will be removed in mlpack 5.0.0). + const arma::Row* labels; //! Number of target points. size_t k; @@ -122,14 +221,14 @@ class LMNN //! Regularization value. double regularization; - //! Range after which impostors need to be recalculated. - size_t range; + //! Number of iterations after which impostors need to be recalculated. + size_t updateInterval; //! Distance to be used. DistanceType distance; - //! The optimizer to use. - OptimizerType optimizer; + //! The optimizer to use (will be removed in mlpack 5.0.0). + DeprecatedOptimizerType optimizer; }; // class LMNN } // namespace mlpack diff --git a/src/mlpack/methods/lmnn/lmnn_function.hpp b/src/mlpack/methods/lmnn/lmnn_function.hpp index f35fcf8cd0..e64b8ae0d0 100644 --- a/src/mlpack/methods/lmnn/lmnn_function.hpp +++ b/src/mlpack/methods/lmnn/lmnn_function.hpp @@ -41,9 +41,22 @@ namespace mlpack { * operate on one point in the dataset. This is useful for optimizers like * stochastic gradient descent (see ens::SGD). */ -template +template, + typename DistanceType = SquaredEuclideanDistance> class LMNNFunction { + // Convenience typedef for element type of data. + typedef typename MatType::elem_type ElemType; + // Convenience typedef for column vector of data. + typedef typename GetColType::type VecType; + // Convenience typedef for cube of data. + typedef typename GetCubeType::type CubeType; + // Convenience typedef for dense matrix of indices. + typedef typename GetUDenseMatType::type UMatType; + // Convenience typedef for dense vector of indices. + typedef typename GetColType::type UVecType; + public: /** * Constructor for LMNNFunction class. @@ -52,14 +65,14 @@ class LMNNFunction * @param labels Input dataset labels. * @param k Number of target neighbors to be used. * @param regularization Regularization value. - * @param range Range after which impostors need to be recalculated. + * @param updateInterval Number of iterations before impostors are recomputed. * @param distance Type of distance metric used for computation. */ - LMNNFunction(const arma::mat& dataset, - const arma::Row& labels, + LMNNFunction(const MatType& dataset, + const LabelsType& labels, size_t k, double regularization, - size_t range, + size_t updateInterval, DistanceType distance = DistanceType()); @@ -69,13 +82,13 @@ class LMNNFunction void Shuffle(); /** - * Evaluate the LMNN function for the given transformation matrix. This is the - * non-separable implementation, where the objective function is not + * Evaluate the LMNN function for the given transformation matrix. This is + * the non-separable implementation, where the objective function is not * decomposed into the sum of several objective functions. * * @param transformation Transformation matrix of Mahalanobis distance. */ - double Evaluate(const arma::mat& transformation); + ElemType Evaluate(const MatType& transformation); /** * Evaluate the LMNN objective function for the given transformation matrix on @@ -89,9 +102,9 @@ class LMNNFunction * @param begin Index of the initial point to use for objective function. * @param batchSize Number of points to use for objective function. */ - double Evaluate(const arma::mat& transformation, - const size_t begin, - const size_t batchSize = 1); + ElemType Evaluate(const MatType& transformation, + const size_t begin, + const size_t batchSize = 1); /** * Evaluate the gradient of the LMNN function for the given transformation @@ -103,7 +116,7 @@ class LMNNFunction * @param gradient Matrix to store the calculated gradient in. */ template - void Gradient(const arma::mat& transformation, GradType& gradient); + void Gradient(const MatType& transformation, GradType& gradient); /** * Evaluate the gradient of the LMNN function for the given transformation @@ -121,7 +134,7 @@ class LMNNFunction * @param batchSize Number of points to use for objective function. */ template - void Gradient(const arma::mat& transformation, + void Gradient(const MatType& transformation, const size_t begin, GradType& gradient, const size_t batchSize = 1); @@ -137,8 +150,8 @@ class LMNNFunction * @param gradient Matrix to store the calculated gradient in. */ template - double EvaluateWithGradient(const arma::mat& transformation, - GradType& gradient); + ElemType EvaluateWithGradient(const MatType& transformation, + GradType& gradient); /** * Evaluate the LMNN objective function together with gradient for the given @@ -156,13 +169,13 @@ class LMNNFunction * @param batchSize Number of points to use for objective function. */ template - double EvaluateWithGradient(const arma::mat& transformation, - const size_t begin, - GradType& gradient, - const size_t batchSize = 1); + ElemType EvaluateWithGradient(const MatType& transformation, + const size_t begin, + GradType& gradient, + const size_t batchSize = 1); //! Return the initial point for the optimization. - const arma::mat& GetInitialPoint() const { return initialPoint; } + const MatType& GetInitialPoint() const { return initialPoint; } /** * Get the number of functions the objective function can be decomposed into. @@ -171,7 +184,7 @@ class LMNNFunction size_t NumFunctions() const { return dataset.n_cols; } //! Return the dataset passed into the constructor. - const arma::mat& Dataset() const { return dataset; } + const MatType& Dataset() const { return dataset; } //! Access the regularization value. const double& Regularization() const { return regularization; } @@ -183,26 +196,26 @@ class LMNNFunction //! Modify the value of k. size_t& K() { return k; } - //! Access the value of range. - const size_t& Range() const { return range; } - //! Modify the value of k. - size_t& Range() { return range; } + //! Access the number of iterations between impostor recomputation. + const size_t& UpdateInterval() const { return updateInterval; } + //! Modify the number of iterations between impostor recomputation.. + size_t& UpdateInterval() { return updateInterval; } private: //! data. This will be an alias until Shuffle() is called. - arma::mat dataset; + MatType dataset; //! labels. This will be an alias until Shuffle() is called. - arma::Row labels; + LabelsType labels; //! Initial parameter point. - arma::mat initialPoint; + MatType initialPoint; //! Store transformed dataset. - arma::mat transformedDataset; + MatType transformedDataset; //! Store target neighbors of data points. - arma::Mat targetNeighbors; + UMatType targetNeighbors; //! Initial impostors. - arma::Mat impostors; + UMatType impostors; //! Cache distance. Used to avoid repetive calculation. - arma::mat distanceMat; + MatType distanceMat; //! Number of target neighbors. size_t k; //! The instantiated distance metric. @@ -211,28 +224,28 @@ class LMNNFunction double regularization; //! Keep iterations count. size_t iteration; - //! Range after which impostors need to be recalculated. - size_t range; + //! Number of iterations before impostors need to be recalculated. + size_t updateInterval; //! Constraints Object. - Constraints constraint; + Constraints constraint; //! Holds pre-calculated cij. - arma::mat pCij; + MatType pCij; //! Holds the norm of each data point. - arma::vec norm; + VecType norm; //! Hold previous eval values for each datapoint. - arma::cube evalOld; + CubeType evalOld; //! Hold previous maximum norm of impostor. - arma::mat maxImpNorm; + MatType maxImpNorm; //! Holds previous transformation matrix. Used for L-BFGS like optimizer. - arma::mat transformationOld; + MatType transformationOld; //! Holds previous transformation matrices. - std::vector oldTransformationMatrices; + std::vector oldTransformationMatrices; //! Holds number of points which are using each transformation matrix. std::vector oldTransformationCounts; //! Holds points to transformation matrix mapping. - arma::vec lastTransformationIndices; + VecType lastTransformationIndices; //! Used for storing points to re-calculate impostors for. - arma::uvec points; + UVecType points; //! Flag for controlling use of bounds over impostors. bool impBounds; /** @@ -242,12 +255,12 @@ class LMNNFunction */ inline void Precalculate(); //! Update cache transformation matrices. - inline void UpdateCache(const arma::mat& transformation, + inline void UpdateCache(const MatType& transformation, const size_t begin, const size_t batchSize); //! Calculate norm of change in transformation. - inline void TransDiff(std::map& transformationDiffs, - const arma::mat& transformation, + inline void TransDiff(std::unordered_map& transDiffs, + const MatType& transformation, const size_t begin, const size_t batchSize); }; diff --git a/src/mlpack/methods/lmnn/lmnn_function_impl.hpp b/src/mlpack/methods/lmnn/lmnn_function_impl.hpp index 01aa77afea..58dc54011c 100644 --- a/src/mlpack/methods/lmnn/lmnn_function_impl.hpp +++ b/src/mlpack/methods/lmnn/lmnn_function_impl.hpp @@ -18,18 +18,19 @@ namespace mlpack { -template -LMNNFunction::LMNNFunction(const arma::mat& datasetIn, - const arma::Row& labelsIn, - size_t k, - double regularization, - size_t range, - DistanceType distance) : +template +LMNNFunction::LMNNFunction( + const MatType& datasetIn, + const LabelsType& labelsIn, + size_t k, + double regularization, + size_t updateInterval, + DistanceType distance) : k(k), distance(distance), regularization(regularization), iteration(0), - range(range), + updateInterval(updateInterval), constraint(datasetIn, labelsIn, k), points(datasetIn.n_cols), impBounds(false) @@ -60,7 +61,7 @@ LMNNFunction::LMNNFunction(const arma::mat& datasetIn, lastTransformationIndices.zeros(); // Reserve the first element of cache. - arma::mat emptyMat; + MatType emptyMat; oldTransformationMatrices.push_back(emptyMat); oldTransformationCounts.push_back(dataset.n_cols); @@ -92,18 +93,18 @@ LMNNFunction::LMNNFunction(const arma::mat& datasetIn, } //! Shuffle the dataset. -template -void LMNNFunction::Shuffle() +template +void LMNNFunction::Shuffle() { - arma::mat newDataset = dataset; - arma::Mat newLabels = labels; - arma::cube newEvalOld = evalOld; - arma::vec newlastTransformationIndices = lastTransformationIndices; - arma::mat newMaxImpNorm = maxImpNorm; - arma::vec newNorm = norm; + MatType newDataset = dataset; + LabelsType newLabels = labels; + CubeType newEvalOld = evalOld; + VecType newlastTransformationIndices = lastTransformationIndices; + MatType newMaxImpNorm = maxImpNorm; + VecType newNorm = norm; // Generate ordering. - arma::uvec ordering = arma::shuffle(arma::linspace(0, + UVecType ordering = arma::shuffle(arma::linspace(0, dataset.n_cols - 1, dataset.n_cols)); ClearAlias(dataset); @@ -126,9 +127,9 @@ void LMNNFunction::Shuffle() } // Update cache transformation matrices. -template -inline void LMNNFunction::UpdateCache( - const arma::mat& transformation, +template +inline void LMNNFunction::UpdateCache( + const MatType& transformation, const size_t begin, const size_t batchSize) { @@ -162,31 +163,13 @@ inline void LMNNFunction::UpdateCache( } oldTransformationCounts[index] += batchSize; - - #ifdef DEBUG - size_t total = 0; - for (size_t i = 1; i < oldTransformationCounts.size(); ++i) - { - std::ostringstream oss; - oss << "transformation counts for matrix " << i - << " invalid (" << oldTransformationCounts[i] << ")!"; - Log::Assert(oldTransformationCounts[i] <= dataset.n_cols, oss.str()); - total += oldTransformationCounts[i]; - } - - std::ostringstream oss; - oss << "total count for transformation matrices invalid (" << total - << ", " << "should be " << dataset.n_cols << "!"; - if (begin + batchSize == dataset.n_cols) - Log::Assert(total == dataset.n_cols, oss.str()); - #endif } // Calculate norm of change in transformation. -template -inline void LMNNFunction::TransDiff( - std::map& transformationDiffs, - const arma::mat& transformation, +template +inline void LMNNFunction::TransDiff( + std::unordered_map& transformationDiffs, + const MatType& transformation, const size_t begin, const size_t batchSize) { @@ -209,22 +192,24 @@ inline void LMNNFunction::TransDiff( } //! Evaluate cost over whole dataset. -template -double LMNNFunction::Evaluate(const arma::mat& transformation) +template +typename MatType::elem_type +LMNNFunction::Evaluate( + const MatType& transformation) { - double cost = 0; + ElemType cost = 0; // Apply distance metric over dataset. transformedDataset = transformation * dataset; - double transformationDiff = 0; + ElemType transformationDiff = 0; if (!transformationOld.is_empty()) { // Calculate norm of change in transformation. transformationDiff = arma::norm(transformation - transformationOld); } - if (!transformationOld.is_empty() && iteration++ % range == 0) + if (!transformationOld.is_empty() && iteration++ % updateInterval == 0) { if (impBounds) { @@ -251,7 +236,7 @@ double LMNNFunction::Evaluate(const arma::mat& transformation) norm); } } - else if (iteration++ % range == 0) + else if (iteration++ % updateInterval == 0) { // Re-calculate impostors on transformed dataset. constraint.Impostors(impostors, distanceMat, transformedDataset, labels, @@ -263,7 +248,7 @@ double LMNNFunction::Evaluate(const arma::mat& transformation) for (size_t j = 0; j < k ; ++j) { // Calculate cost due to distance between target neighbors & data point. - double eval = distance.Evaluate(transformedDataset.col(i), + ElemType eval = distance.Evaluate(transformedDataset.col(i), transformedDataset.col(targetNeighbors(j, i))); cost += (1 - regularization) * eval; } @@ -276,7 +261,7 @@ double LMNNFunction::Evaluate(const arma::mat& transformation) { // Calculate cost due to {data point, target neighbors, impostors} // triplets. - double eval = 0; + ElemType eval = 0; // Bounds for eval. if (!transformationOld.is_empty() && evalOld(l, j, i) < -1) @@ -292,7 +277,7 @@ double LMNNFunction::Evaluate(const arma::mat& transformation) // Calculate exact eval value. if (eval > -1) { - if (iteration - 1 % range == 0) + if (iteration - 1 % updateInterval == 0) { eval = distance.Evaluate(transformedDataset.col(i), transformedDataset.col(targetNeighbors(j, i))) - @@ -338,21 +323,23 @@ double LMNNFunction::Evaluate(const arma::mat& transformation) } //! Calculate cost over batches. -template -double LMNNFunction::Evaluate(const arma::mat& transformation, - const size_t begin, - const size_t batchSize) +template +typename MatType::elem_type +LMNNFunction::Evaluate( + const MatType& transformation, + const size_t begin, + const size_t batchSize) { - double cost = 0; + ElemType cost = 0; // Calculate norm of change in transformation. - std::map transformationDiffs; + std::unordered_map transformationDiffs; TransDiff(transformationDiffs, transformation, begin, batchSize); // Apply distance metric over dataset. transformedDataset = transformation * dataset; - if (impBounds && iteration++ % range == 0) + if (impBounds && iteration++ % updateInterval == 0) { // Track number of data points to use for impostors calculatiom. size_t numPoints = 0; @@ -378,7 +365,7 @@ double LMNNFunction::Evaluate(const arma::mat& transformation, constraint.Impostors(impostors, distanceMat, transformedDataset, labels, norm, points, numPoints); } - else if (iteration++ % range == 0) + else if (iteration++ % updateInterval == 0) { // Re-calculate impostors on transformed dataset. constraint.Impostors(impostors, distanceMat, transformedDataset, labels, @@ -390,7 +377,7 @@ double LMNNFunction::Evaluate(const arma::mat& transformation, for (size_t j = 0; j < k ; ++j) { // Calculate cost due to distance between target neighbors & data point. - double eval = distance.Evaluate(transformedDataset.col(i), + ElemType eval = distance.Evaluate(transformedDataset.col(i), transformedDataset.col(targetNeighbors(j, i))); cost += (1 - regularization) * eval; } @@ -403,7 +390,7 @@ double LMNNFunction::Evaluate(const arma::mat& transformation, { // Calculate cost due to {data point, target neighbors, impostors} // triplets. - double eval = 0; + ElemType eval = 0; // Bounds for eval. if (lastTransformationIndices(i) && evalOld(l, j, i) < -1) @@ -419,7 +406,7 @@ double LMNNFunction::Evaluate(const arma::mat& transformation, // Calculate exact eval value. if (eval > -1) { - if (iteration - 1 % range == 0) + if (iteration - 1 % updateInterval == 0) { eval = distance.Evaluate(transformedDataset.col(i), transformedDataset.col(targetNeighbors(j, i))) - @@ -467,16 +454,16 @@ double LMNNFunction::Evaluate(const arma::mat& transformation, } //! Compute gradient over whole dataset. -template +template template -void LMNNFunction::Gradient(const arma::mat& transformation, - GradType& gradient) +void LMNNFunction::Gradient( + const MatType& transformation, GradType& gradient) { // Apply distance metric over dataset. transformedDataset = transformation * dataset; - double transformationDiff = 0; - if (!transformationOld.is_empty() && iteration++ % range == 0) + ElemType transformationDiff = 0; + if (!transformationOld.is_empty() && iteration++ % updateInterval == 0) { // Calculate norm of change in transformation. transformationDiff = arma::norm(transformation - transformationOld); @@ -506,7 +493,7 @@ void LMNNFunction::Gradient(const arma::mat& transformation, norm); } } - else if (iteration++ % range == 0) + else if (iteration++ % updateInterval == 0) { // Re-calculate impostors on transformed dataset. constraint.Impostors(impostors, distanceMat, transformedDataset, labels, @@ -516,10 +503,10 @@ void LMNNFunction::Gradient(const arma::mat& transformation, gradient.zeros(transformation.n_rows, transformation.n_cols); // Calculate gradient due to target neighbors. - arma::mat cij = pCij; + MatType cij = pCij; // Calculate gradient due to impostors. - arma::mat cil = zeros(dataset.n_rows, dataset.n_rows); + MatType cil = zeros(dataset.n_rows, dataset.n_rows); for (size_t i = 0; i < dataset.n_cols; ++i) { @@ -530,7 +517,7 @@ void LMNNFunction::Gradient(const arma::mat& transformation, { // Calculate cost due to {data point, target neighbors, impostors} // triplets. - double eval = 0; + ElemType eval = 0; // Bounds for eval. if (!transformationOld.is_empty() && evalOld(l, j, i) < -1) @@ -546,7 +533,7 @@ void LMNNFunction::Gradient(const arma::mat& transformation, // Calculate exact eval value. if (eval > -1) { - if (iteration - 1 % range == 0) + if (iteration - 1 % updateInterval == 0) { eval = distance.Evaluate(transformedDataset.col(i), transformedDataset.col(targetNeighbors(j, i))) - @@ -581,7 +568,7 @@ void LMNNFunction::Gradient(const arma::mat& transformation, } // Caculate gradient due to impostors. - arma::vec diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); + VecType diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); cil += diff * trans(diff); diff = dataset.col(i) - dataset.col(impostors(l, i)); @@ -598,21 +585,22 @@ void LMNNFunction::Gradient(const arma::mat& transformation, } //! Compute gradient over a batch of data points. -template +template template -void LMNNFunction::Gradient(const arma::mat& transformation, - const size_t begin, - GradType& gradient, - const size_t batchSize) +void LMNNFunction::Gradient( + const MatType& transformation, + const size_t begin, + GradType& gradient, + const size_t batchSize) { // Apply distance metric over dataset. transformedDataset = transformation * dataset; // Calculate norm of change in transformation. - std::map transformationDiffs; + std::unordered_map transformationDiffs; TransDiff(transformationDiffs, transformation, begin, batchSize); - if (impBounds && iteration++ % range == 0) + if (impBounds && iteration++ % updateInterval == 0) { // Track number of data points to use for impostors calculatiom. size_t numPoints = 0; @@ -638,7 +626,7 @@ void LMNNFunction::Gradient(const arma::mat& transformation, constraint.Impostors(impostors, distanceMat, transformedDataset, labels, norm, points, numPoints); } - else if (iteration++ % range == 0) + else if (iteration++ % updateInterval == 0) { // Re-calculate impostors on transformed dataset. constraint.Impostors(impostors, distanceMat, transformedDataset, labels, @@ -647,15 +635,15 @@ void LMNNFunction::Gradient(const arma::mat& transformation, gradient.zeros(transformation.n_rows, transformation.n_cols); - arma::mat cij = zeros(dataset.n_rows, dataset.n_rows); - arma::mat cil = zeros(dataset.n_rows, dataset.n_rows); + MatType cij = zeros(dataset.n_rows, dataset.n_rows); + MatType cil = zeros(dataset.n_rows, dataset.n_rows); for (size_t i = begin; i < begin + batchSize; ++i) { for (size_t j = 0; j < k ; ++j) { // Calculate gradient due to target neighbors. - arma::vec diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); + VecType diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); cij += diff * trans(diff); } @@ -666,7 +654,7 @@ void LMNNFunction::Gradient(const arma::mat& transformation, { // Calculate cost due to {data point, target neighbors, impostors} // triplets. - double eval = 0; + ElemType eval = 0; // Bounds for eval. if (lastTransformationIndices(i) && evalOld(l, j, i) < -1) @@ -682,7 +670,7 @@ void LMNNFunction::Gradient(const arma::mat& transformation, // Calculate exact eval value. if (eval > -1) { - if (iteration - 1 % range == 0) + if (iteration - 1 % updateInterval == 0) { eval = distance.Evaluate(transformedDataset.col(i), transformedDataset.col(targetNeighbors(j, i))) - @@ -719,7 +707,7 @@ void LMNNFunction::Gradient(const arma::mat& transformation, } // Caculate gradient due to impostors. - arma::vec diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); + VecType diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); cil += diff * trans(diff); diff = dataset.col(i) - dataset.col(impostors(l, i)); @@ -736,25 +724,26 @@ void LMNNFunction::Gradient(const arma::mat& transformation, } //! Compute cost & gradient over whole dataset. -template +template template -double LMNNFunction::EvaluateWithGradient( - const arma::mat& transformation, +typename MatType::elem_type +LMNNFunction::EvaluateWithGradient( + const MatType& transformation, GradType& gradient) { - double cost = 0; + ElemType cost = 0; // Apply distance metric over dataset. transformedDataset = transformation * dataset; - double transformationDiff = 0; + ElemType transformationDiff = 0; if (!transformationOld.is_empty()) { // Calculate norm of change in transformation. transformationDiff = arma::norm(transformation - transformationOld); } - if (!transformationOld.is_empty() && iteration++ % range == 0) + if (!transformationOld.is_empty() && iteration++ % updateInterval == 0) { if (impBounds) { @@ -781,7 +770,7 @@ double LMNNFunction::EvaluateWithGradient( norm); } } - else if (iteration++ % range == 0) + else if (iteration++ % updateInterval == 0) { // Re-calculate impostors on transformed dataset. constraint.Impostors(impostors, distanceMat, transformedDataset, labels, @@ -791,17 +780,17 @@ double LMNNFunction::EvaluateWithGradient( gradient.zeros(transformation.n_rows, transformation.n_cols); // Calculate gradient due to target neighbors. - arma::mat cij = pCij; + MatType cij = pCij; // Calculate gradient due to impostors. - arma::mat cil = zeros(dataset.n_rows, dataset.n_rows); + MatType cil = zeros(dataset.n_rows, dataset.n_rows); for (size_t i = 0; i < dataset.n_cols; ++i) { for (size_t j = 0; j < k ; ++j) { // Calculate cost due to distance between target neighbors & data point. - double eval = distance.Evaluate(transformedDataset.col(i), + ElemType eval = distance.Evaluate(transformedDataset.col(i), transformedDataset.col(targetNeighbors(j, i))); cost += (1 - regularization) * eval; } @@ -813,7 +802,7 @@ double LMNNFunction::EvaluateWithGradient( { // Calculate cost due to {data point, target neighbors, impostors} // triplets. - double eval = 0; + ElemType eval = 0; // Bounds for eval. if (!transformationOld.is_empty() && evalOld(l, j, i) < -1) @@ -829,7 +818,7 @@ double LMNNFunction::EvaluateWithGradient( // Calculate exact eval value. if (eval > -1) { - if (iteration - 1 % range == 0) + if (iteration - 1 % updateInterval == 0) { eval = distance.Evaluate(transformedDataset.col(i), transformedDataset.col(targetNeighbors(j, i))) - @@ -858,7 +847,7 @@ double LMNNFunction::EvaluateWithGradient( cost += regularization * (1 + eval); // Caculate gradient due to impostors. - arma::vec diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); + VecType diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); cil += diff * trans(diff); diff = dataset.col(i) - dataset.col(impostors(l, i)); @@ -877,24 +866,25 @@ double LMNNFunction::EvaluateWithGradient( } //! Compute cost & gradient over a batch of data points. -template +template template -double LMNNFunction::EvaluateWithGradient( - const arma::mat& transformation, +typename MatType::elem_type +LMNNFunction::EvaluateWithGradient( + const MatType& transformation, const size_t begin, GradType& gradient, const size_t batchSize) { - double cost = 0; + ElemType cost = 0; // Calculate norm of change in transformation. - std::map transformationDiffs; + std::unordered_map transformationDiffs; TransDiff(transformationDiffs, transformation, begin, batchSize); // Apply distance metric over dataset. transformedDataset = transformation * dataset; - if (impBounds && iteration++ % range == 0) + if (impBounds && iteration++ % updateInterval == 0) { // Track number of data points to use for impostors calculatiom. size_t numPoints = 0; @@ -920,7 +910,7 @@ double LMNNFunction::EvaluateWithGradient( constraint.Impostors(impostors, distanceMat, transformedDataset, labels, norm, points, numPoints); } - else if (iteration++ % range == 0) + else if (iteration++ % updateInterval == 0) { // Re-calculate impostors on transformed dataset. constraint.Impostors(impostors, distanceMat, transformedDataset, labels, @@ -929,20 +919,20 @@ double LMNNFunction::EvaluateWithGradient( gradient.zeros(transformation.n_rows, transformation.n_cols); - arma::mat cij = zeros(dataset.n_rows, dataset.n_rows); - arma::mat cil = zeros(dataset.n_rows, dataset.n_rows); + MatType cij = zeros(dataset.n_rows, dataset.n_rows); + MatType cil = zeros(dataset.n_rows, dataset.n_rows); for (size_t i = begin; i < begin + batchSize; ++i) { for (size_t j = 0; j < k ; ++j) { // Calculate cost due to distance between target neighbors & data point. - double eval = distance.Evaluate(transformedDataset.col(i), + ElemType eval = distance.Evaluate(transformedDataset.col(i), transformedDataset.col(targetNeighbors(j, i))); cost += (1 - regularization) * eval; // Calculate gradient due to target neighbors. - arma::vec diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); + VecType diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); cij += diff * trans(diff); } @@ -953,7 +943,7 @@ double LMNNFunction::EvaluateWithGradient( { // Calculate cost due to {data point, target neighbors, impostors} // triplets. - double eval = 0; + ElemType eval = 0; // Bounds for eval. if (lastTransformationIndices(i) && evalOld(l, j, i) < -1) @@ -969,7 +959,7 @@ double LMNNFunction::EvaluateWithGradient( // Calculate exact eval value. if (eval > -1) { - if (iteration - 1 % range == 0) + if (iteration - 1 % updateInterval == 0) { eval = distance.Evaluate(transformedDataset.col(i), transformedDataset.col(targetNeighbors(j, i))) - @@ -998,7 +988,7 @@ double LMNNFunction::EvaluateWithGradient( cost += regularization * (1 + eval); // Caculate gradient due to impostors. - arma::vec diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); + VecType diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); cil += diff * trans(diff); diff = dataset.col(i) - dataset.col(impostors(l, i)); @@ -1016,8 +1006,8 @@ double LMNNFunction::EvaluateWithGradient( return cost; } -template -inline void LMNNFunction::Precalculate() +template +inline void LMNNFunction::Precalculate() { pCij.zeros(dataset.n_rows, dataset.n_rows); @@ -1026,7 +1016,7 @@ inline void LMNNFunction::Precalculate() for (size_t j = 0; j < k ; ++j) { // Calculate gradient due to target neighbors. - arma::vec diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); + VecType diff = dataset.col(i) - dataset.col(targetNeighbors(j, i)); pCij += diff * trans(diff); } } diff --git a/src/mlpack/methods/lmnn/lmnn_impl.hpp b/src/mlpack/methods/lmnn/lmnn_impl.hpp index 740a3a6c0c..5dfc87bb78 100644 --- a/src/mlpack/methods/lmnn/lmnn_impl.hpp +++ b/src/mlpack/methods/lmnn/lmnn_impl.hpp @@ -21,27 +21,83 @@ namespace mlpack { * Takes in a reference to the dataset. Copies the data, initializes * all of the member variables and constraint object and generate constraints. */ -template -LMNN::LMNN(const arma::mat& dataset, - const arma::Row& labels, - const size_t k, - const DistanceType distance) : - dataset(dataset), - labels(labels), +template +LMNN::LMNN( + const arma::mat& dataset, + const arma::Row& labels, + const size_t k, + const DistanceType distance) : + dataset(&dataset), + labels(&labels), k(k), regularization(0.5), - range(1), + updateInterval(1), distance(distance) { /* nothing to do */ } -template -template -void LMNN::LearnDistance(arma::mat& outputMatrix, +template +LMNN::LMNN( + const size_t k, + const double regularization, + const size_t updateInterval, + const DistanceType distance) : + k(k), + regularization(regularization), + updateInterval(updateInterval), + distance(distance) +{ /* nothing to do */ } + +template +template +void LMNN::LearnDistance( + arma::mat& outputMatrix, CallbackTypes&&... callbacks) +{ + if (!dataset || !labels) + { + throw std::runtime_error("LMNN::LearnDistance(): cannot call without a " + "dataset!"); + } + + LearnDistance(*dataset, *labels, outputMatrix, optimizer, + std::forward(callbacks)...); +} + +template +template +void LMNN::LearnDistance( + const MatType& dataset, + const LabelsType& labels, + MatType& outputMatrix, + CallbackTypes&&... callbacks) const +{ + // This should be replaced with ens::StandardSGD when the deprecated members + // are removed for mlpack 5.0.0. + DeprecatedOptimizerType opt; + LearnDistance(dataset, labels, outputMatrix, opt, + std::forward(callbacks)...); +} + +template +template +void LMNN::LearnDistance( + const MatType& dataset, + const LabelsType& labels, + MatType& outputMatrix, + OptimizerType& opt, + CallbackTypes&&... callbacks) const { // LMNN objective function. - LMNNFunction objFunction(dataset, labels, k, - regularization, range); + LMNNFunction objFunction(dataset, labels, + k, regularization, updateInterval); // See if we were passed an initialized matrix. outputMatrix (L) must be // having r x d dimensionality. @@ -49,15 +105,23 @@ void LMNN::LearnDistance(arma::mat& outputMatrix, (outputMatrix.n_rows > dataset.n_rows) || !(arma::is_finite(outputMatrix))) { - Log::Info << "Initial learning point have invalid dimensionality. " - "Identity matrix will be used as initial learning point for " - "optimization." << std::endl; outputMatrix.eye(dataset.n_rows, dataset.n_rows); } - optimizer.Optimize(objFunction, outputMatrix, callbacks...); + opt.Optimize(objFunction, outputMatrix, callbacks...); } +// Serialize the LMNN object. +template +template +void LMNN::serialize( + Archive& ar, const unsigned int /* version */) +{ + ar(CEREAL_NVP(k)); + ar(CEREAL_NVP(regularization)); + ar(CEREAL_NVP(updateInterval)); + ar(CEREAL_NVP(distance)); +} } // namespace mlpack diff --git a/src/mlpack/methods/lmnn/lmnn_main.cpp b/src/mlpack/methods/lmnn/lmnn_main.cpp index 821c4bd62d..0c270624e6 100644 --- a/src/mlpack/methods/lmnn/lmnn_main.cpp +++ b/src/mlpack/methods/lmnn/lmnn_main.cpp @@ -57,7 +57,7 @@ BINDING_LONG_DESC( PRINT_PARAM_STRING("regularization") + "), In addition, this " "implementation of LMNN includes a parameter to decide the interval " "after which impostors must be re-calculated (specified with " + - PRINT_PARAM_STRING("range") + ")." + PRINT_PARAM_STRING("update_interval") + ")." "\n\n" "Output can either be the learned distance matrix (specified with " + PRINT_PARAM_STRING("output") +"), or the transformed dataset " @@ -124,11 +124,11 @@ BINDING_EXAMPLE( PRINT_CALL("lmnn", "input", "iris", "labels", "iris_labels", "k", 3, "optimizer", "bbsgd", "output", "output") + "\n\n" - "An another program call making use of range & regularization parameter " - "with dataset having labels as last column can be made as: " + "Another program call making use of update interval & regularization " + "parameter with dataset having labels as last column can be made as: " "\n\n" + PRINT_CALL("lmnn", "input", "letter_recognition", "k", 5, - "range", 10, "regularization", 0.4, "output", "output")); + "update_interval", 10, "regularization", 0.4, "output", "output")); // See also... BINDING_SEE_ALSO("@nca", "#nca"); @@ -174,8 +174,8 @@ PARAM_DOUBLE_IN("step_size", "Step size for AMSGrad, BB_SGD and SGD (alpha).", PARAM_FLAG("linear_scan", "Don't shuffle the order in which data points are " "visited for SGD or mini-batch SGD.", "L"); PARAM_INT_IN("batch_size", "Batch size for mini-batch SGD.", "b", 50); -PARAM_INT_IN("range", "Number of iterations after which impostors needs to be " - "recalculated", "R", 1); +PARAM_INT_IN("update_interval", "Number of iterations after which impostors " + "need to be recalculated.", "R", 1); PARAM_INT_IN("seed", "Random seed. If 0, 'std::time(NULL)' is used.", "s", 0); using namespace mlpack; @@ -264,8 +264,8 @@ void BINDING_FUNCTION(util::Params& params, util::Timers& timers) RequireParamValue(params, "k", [](int x) { return x > 0; }, true, "number of targets must be positive"); - RequireParamValue(params, "range", [](int x) { return x > 0; }, true, - "range must be positive"); + RequireParamValue(params, "update_interval", [](int x) { return x > 0; }, + true, "update interval must be positive"); RequireParamValue(params, "batch_size", [](int x) { return x > 0; }, true, "batch size must be positive"); RequireParamValue(params, "regularization", [](double x) @@ -294,7 +294,7 @@ void BINDING_FUNCTION(util::Params& params, util::Timers& timers) const bool printAccuracy = params.Has("print_accuracy"); const bool shuffle = !params.Has("linear_scan"); const size_t batchSize = (size_t) params.Get("batch_size"); - const size_t range = (size_t) params.Get("range"); + const size_t updateInterval = (size_t) params.Get("update_interval"); const size_t rank = (size_t) params.Get("rank"); // Load data. @@ -359,56 +359,49 @@ void BINDING_FUNCTION(util::Params& params, util::Timers& timers) // Now create the LMNN object and run the optimization. timers.Start("lmnn_optimization"); + LMNN lmnn(k, regularization, updateInterval); if (optimizerType == "amsgrad") { - LMNN> lmnn(data, labels, k); - lmnn.Regularization() = regularization; - lmnn.Range() = range; - lmnn.Optimizer().StepSize() = stepSize; - lmnn.Optimizer().MaxIterations() = passes * data.n_cols; - lmnn.Optimizer().Tolerance() = tolerance; - lmnn.Optimizer().Shuffle() = shuffle; - lmnn.Optimizer().BatchSize() = batchSize; + ens::AMSGrad opt; + opt.StepSize() = stepSize; + opt.MaxIterations() = passes * data.n_cols; + opt.Tolerance() = tolerance; + opt.Shuffle() = shuffle; + opt.BatchSize() = batchSize; - lmnn.LearnDistance(distance); + lmnn.LearnDistance(data, labels, distance, opt); } else if (optimizerType == "bbsgd") { - LMNN, ens::BBS_BB> lmnn(data, labels, k); - lmnn.Regularization() = regularization; - lmnn.Range() = range; - lmnn.Optimizer().StepSize() = stepSize; - lmnn.Optimizer().MaxIterations() = passes * data.n_cols; - lmnn.Optimizer().Tolerance() = tolerance; - lmnn.Optimizer().Shuffle() = shuffle; - lmnn.Optimizer().BatchSize() = batchSize; + ens::BBS_BB opt; + opt.StepSize() = stepSize; + opt.MaxIterations() = passes * data.n_cols; + opt.Tolerance() = tolerance; + opt.Shuffle() = shuffle; + opt.BatchSize() = batchSize; - lmnn.LearnDistance(distance); + lmnn.LearnDistance(data, labels, distance, opt); } else if (optimizerType == "sgd") { // Using SGD is not recommended as the learning matrix can // diverge to inf causing serious memory problems. - LMNN, ens::StandardSGD> lmnn(data, labels, k); - lmnn.Regularization() = regularization; - lmnn.Range() = range; - lmnn.Optimizer().StepSize() = stepSize; - lmnn.Optimizer().MaxIterations() = passes * data.n_cols; - lmnn.Optimizer().Tolerance() = tolerance; - lmnn.Optimizer().Shuffle() = shuffle; - lmnn.Optimizer().BatchSize() = batchSize; + ens::StandardSGD opt; + opt.StepSize() = stepSize; + opt.MaxIterations() = passes * data.n_cols; + opt.Tolerance() = tolerance; + opt.Shuffle() = shuffle; + opt.BatchSize() = batchSize; - lmnn.LearnDistance(distance); + lmnn.LearnDistance(data, labels, distance, opt); } else if (optimizerType == "lbfgs") { - LMNN, ens::L_BFGS> lmnn(data, labels, k); - lmnn.Regularization() = regularization; - lmnn.Range() = range; - lmnn.Optimizer().MaxIterations() = maxIterations; - lmnn.Optimizer().MinGradientNorm() = tolerance; + ens::L_BFGS opt; + opt.MaxIterations() = maxIterations; + opt.MinGradientNorm() = tolerance; - lmnn.LearnDistance(distance); + lmnn.LearnDistance(data, labels, distance, opt); } timers.Stop("lmnn_optimization"); diff --git a/src/mlpack/methods/neighbor_search/neighbor_search.hpp b/src/mlpack/methods/neighbor_search/neighbor_search.hpp index b8f61c0155..d2470b1b19 100644 --- a/src/mlpack/methods/neighbor_search/neighbor_search.hpp +++ b/src/mlpack/methods/neighbor_search/neighbor_search.hpp @@ -222,9 +222,11 @@ class NeighborSearch * @param distances Matrix storing distances of neighbors for each query * point. */ + // TODO: templatize further to remove Armadillo type requirement + template void Search(const MatType& querySet, const size_t k, - arma::Mat& neighbors, + arma::Mat& neighbors, arma::Mat& distances); /** @@ -247,9 +249,11 @@ class NeighborSearch * @param sameSet Denotes whether or not the reference and query sets are the * same. */ + // TODO: templatize further to remove Armadillo type requirement + template void Search(Tree& queryTree, const size_t k, - arma::Mat& neighbors, + arma::Mat& neighbors, arma::Mat& distances, bool sameSet = false); @@ -267,8 +271,10 @@ class NeighborSearch * @param distances Matrix storing distances of neighbors for each query * point. */ + // TODO: templatize further to remove Armadillo type requirement + template void Search(const size_t k, - arma::Mat& neighbors, + arma::Mat& neighbors, arma::Mat& distances); /** @@ -300,8 +306,10 @@ class NeighborSearch * query point. * @return Recall. */ - static double Recall(arma::Mat& foundNeighbors, - arma::Mat& realNeighbors); + // TODO: templatize further to remove Armadillo type requirement + template + static double Recall(arma::Mat& foundNeighbors, + arma::Mat& realNeighbors); //! Return the total number of base case evaluations performed during the last //! search. diff --git a/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp b/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp index c73b7855f0..0aa0a7de0d 100644 --- a/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp +++ b/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp @@ -360,11 +360,12 @@ template class TreeType, template class DualTreeTraversalType, template class SingleTreeTraversalType> +template void NeighborSearch::Search( const MatType& querySet, const size_t k, - arma::Mat& neighbors, + arma::Mat& neighbors, arma::Mat& distances) { if (k > referenceSet->n_cols) @@ -385,7 +386,7 @@ DualTreeTraversalType, SingleTreeTraversalType>::Search( // indices back to their original indices when this computation is finished. // To avoid an extra copy, we will store the neighbors and distances in a // separate matrix. - arma::Mat* neighborPtr = &neighbors; + arma::Mat* neighborPtr = &neighbors; arma::Mat* distancePtr = &distances; // Mapping is only necessary if the tree rearranges points. @@ -394,10 +395,10 @@ DualTreeTraversalType, SingleTreeTraversalType>::Search( if (searchMode == DUAL_TREE_MODE) { distancePtr = new arma::Mat; // Query indices need to be mapped. - neighborPtr = new arma::Mat; + neighborPtr = new arma::Mat; } else if (!oldFromNewReferences.empty()) - neighborPtr = new arma::Mat; // Reference indices need mapping. + neighborPtr = new arma::Mat; // Reference indices need mapping. } // Set the size of the neighbor and distance matrices. @@ -565,11 +566,12 @@ template class TreeType, template class DualTreeTraversalType, template class SingleTreeTraversalType> +template void NeighborSearch::Search( Tree& queryTree, const size_t k, - arma::Mat& neighbors, + arma::Mat& neighbors, arma::Mat& distances, bool sameSet) { @@ -593,10 +595,10 @@ DualTreeTraversalType, SingleTreeTraversalType>::Search( const MatType& querySet = queryTree.Dataset(); // We won't need to map query indices, but will we need to map distances? - arma::Mat* neighborPtr = &neighbors; + arma::Mat* neighborPtr = &neighbors; if (!oldFromNewReferences.empty() && TreeTraits::RearrangesDataset) - neighborPtr = new arma::Mat; + neighborPtr = new arma::Mat; neighborPtr->set_size(k, querySet.n_cols); distances.set_size(k, querySet.n_cols); @@ -644,10 +646,11 @@ template class TreeType, template class DualTreeTraversalType, template class SingleTreeTraversalType> +template void NeighborSearch::Search( const size_t k, - arma::Mat& neighbors, + arma::Mat& neighbors, arma::Mat& distances) { if (k > referenceSet->n_cols) @@ -669,14 +672,14 @@ DualTreeTraversalType, SingleTreeTraversalType>::Search( baseCases = 0; scores = 0; - arma::Mat* neighborPtr = &neighbors; + arma::Mat* neighborPtr = &neighbors; arma::Mat* distancePtr = &distances; if (!oldFromNewReferences.empty() && TreeTraits::RearrangesDataset) { // We will always need to rearrange in this case. distancePtr = new MatType; - neighborPtr = new arma::Mat; + neighborPtr = new arma::Mat; } // Initialize results. @@ -861,10 +864,11 @@ template class TreeType, template class DualTreeTraversalType, template class SingleTreeTraversalType> +template double NeighborSearch::Recall( - arma::Mat& foundNeighbors, - arma::Mat& realNeighbors) + arma::Mat& foundNeighbors, + arma::Mat& realNeighbors) { if (foundNeighbors.n_rows != realNeighbors.n_rows || foundNeighbors.n_cols != realNeighbors.n_cols) diff --git a/src/mlpack/methods/neighbor_search/neighbor_search_rules.hpp b/src/mlpack/methods/neighbor_search/neighbor_search_rules.hpp index ef886663a7..057515ca7a 100644 --- a/src/mlpack/methods/neighbor_search/neighbor_search_rules.hpp +++ b/src/mlpack/methods/neighbor_search/neighbor_search_rules.hpp @@ -63,7 +63,10 @@ class NeighborSearchRules * @param distances Matrix storing distances of neighbors for each query * point. */ - void GetResults(arma::Mat& neighbors, arma::Mat& distances); + // TODO: templatize fully to remove requirement of Armadillo matrix + template + void GetResults(arma::Mat& neighbors, + arma::Mat& distances); /** * Get the distance from the query point to the reference point. diff --git a/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp b/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp index 24fa48c6d5..cfbd350090 100644 --- a/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp +++ b/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp @@ -59,8 +59,9 @@ NeighborSearchRules::NeighborSearchRules( } template +template void NeighborSearchRules::GetResults( - arma::Mat& neighbors, + arma::Mat& neighbors, arma::Mat& distances) { neighbors.set_size(k, querySet.n_cols); @@ -71,7 +72,7 @@ void NeighborSearchRules::GetResults( CandidateList& pqueue = candidates[i]; for (size_t j = 1; j <= k; ++j) { - neighbors(k - j, i) = pqueue.top().second; + neighbors(k - j, i) = (IndexType) pqueue.top().second; distances(k - j, i) = pqueue.top().first; pqueue.pop(); } diff --git a/src/mlpack/tests/callback_test.cpp b/src/mlpack/tests/callback_test.cpp index 942a56ddc4..ae9b912c9d 100644 --- a/src/mlpack/tests/callback_test.cpp +++ b/src/mlpack/tests/callback_test.cpp @@ -151,12 +151,13 @@ TEST_CASE("LMNNWithOptimizerCallback", "[CallbackTest]") " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; arma::Row labels = " 0 0 0 1 1 1 "; - LMNN<> lmnn(dataset, labels, 1); + LMNN<> lmnn(1); arma::mat outputMatrix; std::stringstream stream; - lmnn.LearnDistance(outputMatrix, ens::ProgressBar(70, stream)); + lmnn.LearnDistance(dataset, labels, outputMatrix, + ens::ProgressBar(70, stream)); REQUIRE(stream.str().length() > 0); } diff --git a/src/mlpack/tests/lmnn_test.cpp b/src/mlpack/tests/lmnn_test.cpp index 44eb0b0d66..6b0c0d254a 100644 --- a/src/mlpack/tests/lmnn_test.cpp +++ b/src/mlpack/tests/lmnn_test.cpp @@ -30,25 +30,27 @@ using namespace ens; * The target neighbors function should be correct. * point. */ -TEST_CASE("LMNNTargetNeighborsTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNTargetNeighborsTest", "[LMNNTest]", float, double) { - // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + typedef TestType ElemType; - Constraints<> constraint(dataset, labels, 1); + // Useful but simple dataset with six points and two classes. + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; + + Constraints, arma::Row> constraint(dataset, + labels, 1); // Calculate norm of datapoints. - arma::vec norm(dataset.n_cols); + arma::Col norm(dataset.n_cols); for (size_t i = 0; i < dataset.n_cols; ++i) { norm(i) = arma::norm(dataset.col(i)); } //! Store target neighbors of data points. - arma::Mat targetNeighbors = - arma::Mat(1, dataset.n_cols, arma::fill::zeros); + arma::umat targetNeighbors(1, dataset.n_cols, arma::fill::zeros); constraint.TargetNeighbors(targetNeighbors, dataset, labels, norm); @@ -63,25 +65,27 @@ TEST_CASE("LMNNTargetNeighborsTest", "[LMNNTest]") /** * The impostors function should be correct. */ -TEST_CASE("LMNNImpostorsTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNImpostorsTest", "[LMNNTest]", float, double) { - // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + typedef TestType ElemType; - Constraints<> constraint(dataset, labels, 1); + // Useful but simple dataset with six points and two classes. + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; + + Constraints, arma::Row> constraint(dataset, + labels, 1); // Calculate norm of datapoints. - arma::vec norm(dataset.n_cols); + arma::Col norm(dataset.n_cols); for (size_t i = 0; i < dataset.n_cols; ++i) { norm(i) = arma::norm(dataset.col(i)); } //! Store impostors of data points. - arma::Mat impostors = - arma::Mat(1, dataset.n_cols, arma::fill::zeros); + arma::umat impostors(1, dataset.n_cols, arma::fill::zeros); constraint.Impostors(impostors, dataset, labels, norm); @@ -101,300 +105,339 @@ TEST_CASE("LMNNImpostorsTest", "[LMNNTest]") * The LMNN function should return the identity matrix as its initial * point. */ -TEST_CASE("LMNNInitialPointTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNInitialPointTest", "[LMNNTest]", float, double) { + typedef TestType ElemType; + // Cheap fake dataset. - arma::mat dataset = arma::randu(5, 5); + arma::Mat dataset = arma::randu>(5, 5); arma::Row labels = "0 1 1 0 0"; - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.5, 1); + LMNNFunction> lmnnfn(dataset, labels, 1, 0.5, 1); // Verify the initial point is the identity matrix. - arma::mat initialPoint = lmnnfn.GetInitialPoint(); + const double eps = std::is_same::value ? 1e-4 : 1e-7; + const double margin = std::is_same::value ? 1e-4 : 1e-5; + arma::Mat initialPoint = lmnnfn.GetInitialPoint(); for (int row = 0; row < 5; row++) { for (int col = 0; col < 5; col++) { if (row == col) - REQUIRE(initialPoint(row, col) == Approx(1.0).epsilon(1e-7)); + REQUIRE(initialPoint(row, col) == Approx(1.0).epsilon(eps)); else - REQUIRE(initialPoint(row, col) == Approx(0.0).margin(1e-5)); + REQUIRE(initialPoint(row, col) == Approx(0.0).margin(margin)); } } } /*** - * Ensure non-seprable objective function is right. + * Ensure non-separable objective function is right. */ -TEST_CASE("LMNNInitialEvaluationTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNInitialEvaluationTest", "[LMNNTest]", float, double) { + typedef TestType ElemType; + // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); - double objective = lmnnfn.Evaluate(arma::eye(2, 2)); + ElemType objective = lmnnfn.Evaluate(arma::eye>(2, 2)); // Result calculated by hand. - REQUIRE(objective == Approx(9.456).epsilon(1e-7)); + const double eps = std::is_same::value ? 1e-4 : 1e-7; + REQUIRE(objective == Approx(9.456).epsilon(eps)); } /** - * Ensure non-seprable gradient function is right. + * Ensure non-separable gradient function is right. */ -TEST_CASE("LMNNInitialGradientTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNInitialGradientTest", "[LMNNTest]", float, double) { + typedef TestType ElemType; + // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); - arma::mat gradient; - arma::mat coordinates = arma::eye(2, 2); + arma::Mat gradient; + arma::Mat coordinates = arma::eye>(2, 2); lmnnfn.Gradient(coordinates, gradient); // Result calculated by hand. - REQUIRE(gradient(0, 0) == Approx(-0.288).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).margin(1e-5)); - REQUIRE(gradient(0, 1) == Approx(0.0).margin(1e-5)); - REQUIRE(gradient(1, 1) == Approx(12.0).epsilon(1e-7)); + const double eps = std::is_same::value ? 1e-4 : 1e-7; + const double margin = std::is_same::value ? 1e-4 : 1e-5; + REQUIRE(gradient(0, 0) == Approx(-0.288).epsilon(eps)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(12.0).epsilon(eps)); } /*** - * Ensure non-seprable EvaluateWithGradient function is right. + * Ensure non-separable EvaluateWithGradient function is right. */ -TEST_CASE("LMNNInitialEvaluateWithGradientTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNInitialEvaluateWithGradientTest", "[LMNNTest]", float, + double) { + typedef TestType ElemType; + // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); - arma::mat gradient; - arma::mat coordinates = arma::eye(2, 2); - double objective = lmnnfn.EvaluateWithGradient(coordinates, gradient); + arma::Mat gradient; + arma::Mat coordinates = arma::eye>(2, 2); + ElemType objective = lmnnfn.EvaluateWithGradient(coordinates, gradient); + + const double eps = std::is_same::value ? 1e-4 : 1e-7; + const double margin = std::is_same::value ? 1e-4 : 1e-5; // Result calculated by hand. - REQUIRE(objective == Approx(9.456).epsilon(1e-7)); + REQUIRE(objective == Approx(9.456).epsilon(eps)); // Check Gradient - REQUIRE(gradient(0, 0) == Approx(-0.288).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).margin(1e-5)); - REQUIRE(gradient(0, 1) == Approx(0.0).margin(1e-5)); - REQUIRE(gradient(1, 1) == Approx(12.0).epsilon(1e-7)); + REQUIRE(gradient(0, 0) == Approx(-0.288).epsilon(eps)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(12.0).epsilon(eps)); } /** * Ensure the separable objective function is right. */ -TEST_CASE("LMNNSeparableObjectiveTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNSeparableObjectiveTest", "[LMNNTest]", float, double) { - // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + typedef TestType ElemType; - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + // Useful but simple dataset with six points and two classes. + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; + + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); // Result calculated by hand. - arma::mat coordinates = arma::eye(2, 2); - REQUIRE(lmnnfn.Evaluate(coordinates, 0, 1) == Approx(1.576).epsilon(1e-7)); - REQUIRE(lmnnfn.Evaluate(coordinates, 1, 1) == Approx(1.576).epsilon(1e-7)); - REQUIRE(lmnnfn.Evaluate(coordinates, 2, 1) == Approx(1.576).epsilon(1e-7)); - REQUIRE(lmnnfn.Evaluate(coordinates, 3, 1) == Approx(1.576).epsilon(1e-7)); - REQUIRE(lmnnfn.Evaluate(coordinates, 4, 1) == Approx(1.576).epsilon(1e-7)); - REQUIRE(lmnnfn.Evaluate(coordinates, 5, 1) == Approx(1.576).epsilon(1e-7)); + const double eps = std::is_same::value ? 1e-4 : 1e-7; + arma::Mat coordinates = arma::eye>(2, 2); + REQUIRE(lmnnfn.Evaluate(coordinates, 0, 1) == Approx(1.576).epsilon(eps)); + REQUIRE(lmnnfn.Evaluate(coordinates, 1, 1) == Approx(1.576).epsilon(eps)); + REQUIRE(lmnnfn.Evaluate(coordinates, 2, 1) == Approx(1.576).epsilon(eps)); + REQUIRE(lmnnfn.Evaluate(coordinates, 3, 1) == Approx(1.576).epsilon(eps)); + REQUIRE(lmnnfn.Evaluate(coordinates, 4, 1) == Approx(1.576).epsilon(eps)); + REQUIRE(lmnnfn.Evaluate(coordinates, 5, 1) == Approx(1.576).epsilon(eps)); } /** * Ensure the separable gradient is right. */ -TEST_CASE("LMNNSeparableGradientTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNSeparableGradientTest", "[LMNNTest]", float, double) { + typedef TestType ElemType; + // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); - arma::mat coordinates = arma::eye(2, 2); - arma::mat gradient(2, 2); + arma::Mat coordinates = arma::eye>(2, 2); + arma::Mat gradient(2, 2); lmnnfn.Gradient(coordinates, 0, gradient, 1); - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + const double eps = std::is_same::value ? 1e-4 : 1e-7; + const double margin = std::is_same::value ? 1e-4 : 1e-5; + + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); lmnnfn.Gradient(coordinates, 1, gradient, 1); - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); lmnnfn.Gradient(coordinates, 2, gradient, 1); - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); lmnnfn.Gradient(coordinates, 3, gradient, 1); - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); lmnnfn.Gradient(coordinates, 4, gradient, 1); - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); lmnnfn.Gradient(coordinates, 5, gradient, 1); - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); } /** * Ensure the separable EvaluateWithGradient function is right. */ -TEST_CASE("LMNNSeparableEvaluateWithGradientTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNSeparableEvaluateWithGradientTest", "[LMNNTest]", float, + double) { + typedef TestType ElemType; + // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); - arma::mat coordinates = arma::eye(2, 2); - arma::mat gradient(2, 2); + arma::Mat coordinates = arma::eye>(2, 2); + arma::Mat gradient(2, 2); - double objective = lmnnfn.EvaluateWithGradient(coordinates, 0, gradient, 1); + ElemType objective = lmnnfn.EvaluateWithGradient(coordinates, 0, gradient, 1); - REQUIRE(objective == Approx(1.576).epsilon(1e-7)); + const double eps = std::is_same::value ? 1e-4 : 1e-7; + const double margin = std::is_same::value ? 1e-4 : 1e-5; - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + REQUIRE(objective == Approx(1.576).epsilon(eps)); + + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); objective = lmnnfn.EvaluateWithGradient(coordinates, 1, gradient, 1); - REQUIRE(objective == Approx(1.576).epsilon(1e-7)); + REQUIRE(objective == Approx(1.576).epsilon(eps)); - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); objective = lmnnfn.EvaluateWithGradient(coordinates, 2, gradient, 1); - REQUIRE(objective == Approx(1.576).epsilon(1e-7)); + REQUIRE(objective == Approx(1.576).epsilon(eps)); - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); objective = lmnnfn.EvaluateWithGradient(coordinates, 3, gradient, 1); - REQUIRE(objective == Approx(1.576).epsilon(1e-7)); + REQUIRE(objective == Approx(1.576).epsilon(eps)); - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); objective = lmnnfn.EvaluateWithGradient(coordinates, 4, gradient, 1); - REQUIRE(objective == Approx(1.576).epsilon(1e-7)); + REQUIRE(objective == Approx(1.576).epsilon(eps)); - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); objective = lmnnfn.EvaluateWithGradient(coordinates, 5, gradient, 1); - REQUIRE(objective == Approx(1.576).epsilon(1e-7)); + REQUIRE(objective == Approx(1.576).epsilon(eps)); - REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(1e-7)); - REQUIRE(gradient(0, 1) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 0) == Approx(0.0).epsilon(1e-7)); - REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(1e-7)); + REQUIRE(gradient(0, 0) == Approx(-0.048).epsilon(eps)); + REQUIRE(gradient(0, 1) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 0) == Approx(0.0).margin(margin)); + REQUIRE(gradient(1, 1) == Approx(2.0).epsilon(eps)); } // Check that final objective value using SGD optimizer is optimal. -TEST_CASE("LMNNSGDSimpleDatasetTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNSGDSimpleDatasetTest", "[LMNNTest]", float, double) { + typedef TestType ElemType; + // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; - LMNN<> lmnn(dataset, labels, 1); + LMNN<> lmnn(1); - arma::mat outputMatrix; - lmnn.LearnDistance(outputMatrix); + arma::Mat outputMatrix; + lmnn.LearnDistance(dataset, labels, outputMatrix); // Ensure that the objective function is better now. - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); - double initObj = lmnnfn.Evaluate(arma::eye(2, 2)); - double finalObj = lmnnfn.Evaluate(outputMatrix); + ElemType initObj = lmnnfn.Evaluate(arma::eye>(2, 2)); + ElemType finalObj = lmnnfn.Evaluate(outputMatrix); // finalObj must be less than initObj. REQUIRE(finalObj < initObj); } // Check that final objective value using L-BFGS optimizer is optimal. -TEST_CASE("LMNNLBFGSSimpleDatasetTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNLBFGSSimpleDatasetTest", "[LMNNTest]", float, double) { + typedef TestType ElemType; + // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; - LMNN lmnn(dataset, labels, 1); + LMNN lmnn(1); - arma::mat outputMatrix; - lmnn.LearnDistance(outputMatrix); + arma::Mat outputMatrix; + ens::L_BFGS lbfgs; + lmnn.LearnDistance(dataset, labels, outputMatrix, lbfgs); // Ensure that the objective function is better now. - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); - double initObj = lmnnfn.Evaluate(arma::eye(2, 2)); - double finalObj = lmnnfn.Evaluate(outputMatrix); + ElemType initObj = lmnnfn.Evaluate(arma::eye>(2, 2)); + ElemType finalObj = lmnnfn.Evaluate(outputMatrix); // finalObj must be less than initObj. REQUIRE(finalObj < initObj); } -double KnnAccuracy(const arma::mat& dataset, - const arma::Row& labels, +template +double KnnAccuracy(const MatType& dataset, + const LabelsType& labels, const size_t k) { - arma::Row uniqueLabels = arma::unique(labels); + typedef typename MatType::elem_type ElemType; + + LabelsType uniqueLabels = arma::unique(labels); arma::Mat neighbors; - arma::mat distances; + arma::Mat distances; - KNN knn; + NeighborSearch knn; knn.Train(dataset); knn.Search(k, neighbors, distances); @@ -404,43 +447,44 @@ double KnnAccuracy(const arma::mat& dataset, for (size_t i = 0; i < dataset.n_cols; ++i) { - arma::vec Map; - Map.zeros(uniqueLabels.n_cols); + arma::Col m; + m.zeros(uniqueLabels.n_cols); for (size_t j = 0; j < k; ++j) - Map(labels(neighbors(j, i))) += - 1 / std::pow(distances(j, i) + 1, 2); + m(labels(neighbors(j, i))) += 1 / std::pow(distances(j, i) + 1, 2); - size_t index = ConvTo::From(arma::find(Map - == arma::max(Map))); + size_t index = ConvTo::From(arma::find(m == arma::max(m))); // Increase count if labels match. if (index == labels(i)) count++; } - // return accuracy. + // Return accuracy. return ((double) count / dataset.n_cols) * 100; } // Check that final accuracy is greater than initial accuracy on // simple dataset. -TEST_CASE("LMNNAccuracyTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNAccuracyTest", "[LMNNTest]", float, double) { + typedef TestType ElemType; + // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; // Taking k = 3 as the case of k = 1 can be easily observed. double initAccuracy = KnnAccuracy(dataset, labels, 3); - LMNN<> lmnn(dataset, labels, 2); + LMNN<> lmnn(2); - arma::mat outputMatrix; - lmnn.LearnDistance(outputMatrix); + arma::Mat outputMatrix; + lmnn.LearnDistance(dataset, labels, outputMatrix); - double finalAccuracy = KnnAccuracy(outputMatrix * dataset, labels, 3); + arma::Mat transformedData = outputMatrix * dataset; + double finalAccuracy = KnnAccuracy(transformedData, labels, 3); // finalObj must be less than initObj. REQUIRE(initAccuracy < finalAccuracy); @@ -452,18 +496,20 @@ TEST_CASE("LMNNAccuracyTest", "[LMNNTest]") // Check that accuracy while learning square distance matrix is the same as when // we are learning low rank matrix. I'm ok if this passes only once out of // three tries. -TEST_CASE("LMNNLowRankAccuracyLBFGSTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNLowRankAccuracyLBFGSTest", "[LMNNTest]", float, double) { + typedef TestType ElemType; + bool success = false; for (size_t trial = 0; trial < 3; ++trial) { - arma::mat dataPart1; + arma::Mat dataPart1; dataPart1.randn(5, 50); arma::Row labelsPart1(50); labelsPart1.fill(0); - arma::mat dataPart2; + arma::Mat dataPart2; dataPart2.randn(5, 50); arma::Row labelsPart2(50); @@ -473,26 +519,29 @@ TEST_CASE("LMNNLowRankAccuracyLBFGSTest", "[LMNNTest]") arma::uvec ordering = arma::shuffle(arma::linspace(0, 99, 100)); // Generate datasets. - arma::mat dataset = join_rows(dataPart1, dataPart2); + arma::Mat dataset = join_rows(dataPart1, dataPart2); dataset = dataset.cols(ordering); // Generate labels. arma::Row labels = join_rows(labelsPart1, labelsPart2); labels = labels.cols(ordering); - LMNN lmnn(dataset, labels, 1); + LMNN lmnn(1); // Learn a square matrix. - arma::mat outputMatrix; - lmnn.LearnDistance(outputMatrix); + arma::Mat outputMatrix; + L_BFGS lbfgs; + lmnn.LearnDistance(dataset, labels, outputMatrix, lbfgs); - double acc1 = KnnAccuracy(outputMatrix * dataset, labels, 1); + arma::Mat transformedData = outputMatrix * dataset; + double acc1 = KnnAccuracy(transformedData, labels, 1); // Learn a low rank matrix. - outputMatrix = arma::randu(4, 5); - lmnn.LearnDistance(outputMatrix); + outputMatrix = arma::randu>(4, 5); + lmnn.LearnDistance(dataset, labels, outputMatrix, lbfgs); - double acc2 = KnnAccuracy(outputMatrix * dataset, labels, 1); + transformedData = outputMatrix * dataset; + double acc2 = KnnAccuracy(transformedData, labels, 1); // We keep the tolerance very high. We need to ensure the accuracy drop // isn't any more than 10%. @@ -507,18 +556,20 @@ TEST_CASE("LMNNLowRankAccuracyLBFGSTest", "[LMNNTest]") // Check that accuracy while learning square distance matrix is the same as when // we are learning low rank matrix. I'm ok if this passes only once out of // three tries. -TEST_CASE("LMNNLowRankAccuracyTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNLowRankAccuracyTest", "[LMNNTest]", float, double) { + typedef TestType ElemType; + bool success = false; for (size_t trial = 0; trial < 3; ++trial) { - arma::mat dataPart1; + arma::Mat dataPart1; dataPart1.randn(5, 50); arma::Row labelsPart1(50); labelsPart1.fill(0); - arma::mat dataPart2; + arma::Mat dataPart2; dataPart2.randn(5, 50); arma::Row labelsPart2(50); @@ -528,26 +579,28 @@ TEST_CASE("LMNNLowRankAccuracyTest", "[LMNNTest]") arma::uvec ordering = arma::shuffle(arma::linspace(0, 99, 100)); // Generate datasets. - arma::mat dataset = join_rows(dataPart1, dataPart2); + arma::Mat dataset = join_rows(dataPart1, dataPart2); dataset = dataset.cols(ordering); // Generate labels. arma::Row labels = join_rows(labelsPart1, labelsPart2); labels = labels.cols(ordering); - LMNN<> lmnn(dataset, labels, 1); + LMNN<> lmnn(1); // Learn a square matrix. - arma::mat outputMatrix; - lmnn.LearnDistance(outputMatrix); + arma::Mat outputMatrix; + lmnn.LearnDistance(dataset, labels, outputMatrix); - double acc1 = KnnAccuracy(outputMatrix * dataset, labels, 1); + arma::Mat transformedData = outputMatrix * dataset; + double acc1 = KnnAccuracy(transformedData, labels, 1); // Learn a low rank matrix. - outputMatrix = arma::randu(4, 5); - lmnn.LearnDistance(outputMatrix); + outputMatrix = arma::randu>(4, 5); + lmnn.LearnDistance(dataset, labels, outputMatrix); - double acc2 = KnnAccuracy(outputMatrix * dataset, labels, 1); + transformedData = outputMatrix * dataset; + double acc2 = KnnAccuracy(transformedData, labels, 1); // We keep the tolerance very high. We need to ensure the accuracy drop // isn't any more than 10%. @@ -621,29 +674,31 @@ TEST_CASE("LMNNLowRankAccuracyBBSGDTest", "[LMNNTest]") // Comprehensive gradient tests by Marcus Edel & Ryan Curtin. // Simple numerical gradient checker. -template +template double CheckGradient(FunctionType& function, - arma::mat& coordinates, - const double eps = 1e-7) + MatType& coordinates, + const typename MatType::elem_type eps = 1e-7) { + typedef typename MatType::elem_type ElemType; + // Get gradients for the current parameters. - arma::mat orgGradient, gradient, estGradient; + MatType orgGradient, gradient, estGradient; function.Gradient(coordinates, orgGradient); - estGradient = arma::zeros(orgGradient.n_rows, orgGradient.n_cols); + estGradient = arma::zeros(orgGradient.n_rows, orgGradient.n_cols); // Compute numeric approximations to gradient. for (size_t i = 0; i < orgGradient.n_elem; ++i) { - double tmp = coordinates(i); + ElemType tmp = coordinates(i); // Perturb parameter with a positive constant and get costs. coordinates(i) += eps; - double costPlus = function.Evaluate(coordinates); + ElemType costPlus = function.Evaluate(coordinates); // Perturb parameter with a negative constant and get costs. coordinates(i) -= (2 * eps); - double costMinus = function.Evaluate(coordinates); + ElemType costMinus = function.Evaluate(coordinates); // Restore the parameter value. coordinates(i) = tmp; @@ -657,74 +712,84 @@ double CheckGradient(FunctionType& function, arma::norm(orgGradient + estGradient); } -TEST_CASE("LMNNFunctionGradientTest", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNFunctionGradientTest", "[LMNNTest]", float, double) { + typedef TestType ElemType; + // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; arma::Row labels = " 0 0 0 1 1 1 "; - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); // 10 trials with random positions. for (size_t i = 0; i < 10; ++i) { - arma::mat coordinates(2, 2, arma::fill::randn); + arma::Mat coordinates(2, 2, arma::fill::randn); CheckGradient(lmnnfn, coordinates); } } -TEST_CASE("LMNNFunctionGradientTest2", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNFunctionGradientTest2", "[LMNNTest]", float, double) { - // Useful but simple dataset with six points and two classes. - arma::mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" - " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; - arma::Row labels = " 0 0 0 1 1 1 "; + typedef TestType ElemType; - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + // Useful but simple dataset with six points and two classes. + arma::Mat dataset = "-0.1 -0.1 -0.1 0.1 0.1 0.1;" + " 1.0 0.0 -1.0 1.0 0.0 -1.0 "; + arma::Row labels = " 0 0 0 1 1 1 "; + + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); // 10 trials with random positions. for (size_t i = 0; i < 10; ++i) { - arma::mat coordinates(2, 2, arma::fill::randu); + arma::Mat coordinates(2, 2, arma::fill::randu); CheckGradient(lmnnfn, coordinates); } } -TEST_CASE("LMNNFunctionGradientTest3", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNFunctionGradientTest3", "[LMNNTest]", float, double) { - arma::mat dataset; + typedef TestType ElemType; + + arma::Mat dataset; arma::Row labels; if (!data::Load("iris.csv", dataset)) FAIL("Cannot load dataset iris.csv"); if (!data::Load("iris_labels.txt", labels)) FAIL("Cannot load dataset iris_labels.txt"); - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); // 10 trials with random positions. for (size_t i = 0; i < 10; ++i) { - arma::mat coordinates(dataset.n_rows, dataset.n_rows, arma::fill::randn); + arma::Mat coordinates(dataset.n_rows, dataset.n_rows, + arma::fill::randn); CheckGradient(lmnnfn, coordinates); } } -TEST_CASE("LMNNFunctionGradientTest4", "[LMNNTest]") +TEMPLATE_TEST_CASE("LMNNFunctionGradientTest4", "[LMNNTest]", float, double) { - arma::mat dataset; + typedef TestType ElemType; + + arma::Mat dataset; arma::Row labels; if (!data::Load("iris.csv", dataset)) FAIL("Cannot load dataset iris.csv"); if (!data::Load("iris_labels.txt", labels)) FAIL("Cannot load dataset iris_labels.txt"); - LMNNFunction<> lmnnfn(dataset, labels, 1, 0.6, 1); + LMNNFunction> lmnnfn(dataset, labels, 1, 0.6, 1); // 10 trials with random positions. for (size_t i = 0; i < 10; ++i) { - arma::mat coordinates(dataset.n_rows, dataset.n_rows, arma::fill::randu); + arma::Mat coordinates(dataset.n_rows, dataset.n_rows, + arma::fill::randu); CheckGradient(lmnnfn, coordinates); } } diff --git a/src/mlpack/tests/main_tests/lmnn_test.cpp b/src/mlpack/tests/main_tests/lmnn_test.cpp index 13b68b3d47..901033856a 100644 --- a/src/mlpack/tests/main_tests/lmnn_test.cpp +++ b/src/mlpack/tests/main_tests/lmnn_test.cpp @@ -542,7 +542,7 @@ TEST_CASE_METHOD(LMNNTestFixture, "LMNNDiffRegularizationTest", } /** - * Ensure that different value of range results in a + * Ensure that different value of update interval results in a * different output matrix. */ TEST_CASE_METHOD(LMNNTestFixture, "LMNNDiffRangeTest", @@ -573,7 +573,7 @@ TEST_CASE_METHOD(LMNNTestFixture, "LMNNDiffRangeTest", SetInputParam("input", std::move(inputData)); SetInputParam("labels", std::move(labels)); SetInputParam("linear_scan", (bool) true); - SetInputParam("range", 100); + SetInputParam("update_interval", 100); RUN_BINDING(); @@ -674,9 +674,9 @@ TEST_CASE_METHOD(LMNNTestFixture, "LMNNDiffPassesTest", } /** - * Ensure that number of targets, range, batch size must be always positive - * and regularization, step size, max iterations, rank, passes & tolerance are - * always non-negative + * Ensure that number of targets, update interval, batch size must be always + * positive and regularization, step size, max iterations, rank, passes & + * tolerance are always non-negative. */ TEST_CASE_METHOD(LMNNTestFixture, "LMNNBoundsTest", "[LMNNMainTest][BindingTests]") @@ -701,12 +701,12 @@ TEST_CASE_METHOD(LMNNTestFixture, "LMNNBoundsTest", // Reset settings. ResetSettings(); - // Test for range value. + // Test for update interval value. // Input training data. SetInputParam("input", inputData); SetInputParam("labels", labels); - SetInputParam("range", (int) 0); + SetInputParam("update_interval", (int) 0); REQUIRE_THROWS_AS(RUN_BINDING(), std::runtime_error); From 4a2af775f657c6bab22ca03b18fdb45b3560a192 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 6 Jul 2024 12:41:09 -0400 Subject: [PATCH 089/212] Add links to NCA and LMNN to index and sidebar. --- doc/index.md | 4 ++++ doc/sidebar.html | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/doc/index.md b/doc/index.md index 607a2ae214..cc71edfed4 100644 --- a/doc/index.md +++ b/doc/index.md @@ -123,6 +123,10 @@ Transform data from one space to another. * [`AMF`](user/methods/amf.md): alternating matrix factorization * [`LocalCoordinateCoding`](user/methods/local_coordinate_coding.md): local coordinate coding with dictionary learning + * [`LMNN`](user/methods/lmnn.md): large margin nearest neighbor (distance + metric learning) + * [`NCA`](user/methods/nca.md): neighborhood components analysis (distance + metric learning) * [`NMF`](user/methods/nmf.md): non-negative matrix factorization * [`PCA`](user/methods/pca.md): principal components analysis * [`SparseCoding`](user/methods/sparse_coding.md): sparse coding with diff --git a/doc/sidebar.html b/doc/sidebar.html index 568496310e..86bedf3e6d 100644 --- a/doc/sidebar.html +++ b/doc/sidebar.html @@ -170,6 +170,16 @@ when the sidebar is built for each page. LocalCoordinateCoding
  • +
  • + + LMNN + +
  • +
  • + + NCA + +
  • NMF From ab0ff99e2726a5b75bfa19f7a55799abaf1da42e Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 6 Jul 2024 14:36:41 -0400 Subject: [PATCH 090/212] Update links and rebuild Markdown documentation. --- doc/user/bindings/cli.md | 14 +++++++------- doc/user/bindings/go.md | 10 +++++----- doc/user/bindings/julia.md | 14 +++++++------- doc/user/bindings/python.md | 12 ++++++------ doc/user/bindings/r.md | 12 ++++++------ doc/user/core.md | 4 ++-- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/doc/user/bindings/cli.md b/doc/user/bindings/cli.md index bc02653831..adfd721f22 100644 --- a/doc/user/bindings/cli.md +++ b/doc/user/bindings/cli.md @@ -1677,9 +1677,9 @@ $ mlpack_linear_svm --input_model_file lsvm_model.bin --test_file test.csv $ mlpack_lmnn [--batch_size 50] [--center] [--distance_file ] [--help] [--info ] --input_file [--k 1] [--labels_file ] [--linear_scan] [--max_iterations 100000] [--normalize] - [--optimizer 'amsgrad'] [--passes 50] [--print_accuracy] [--range 1] - [--rank 0] [--regularization 0.5] [--seed 0] [--step_size 0.01] - [--tolerance 1e-07] [--verbose] [--version] [--centered_data_file + [--optimizer 'amsgrad'] [--passes 50] [--print_accuracy] [--rank 0] + [--regularization 0.5] [--seed 0] [--step_size 0.01] [--tolerance 1e-07] + [--update_interval 1] [--verbose] [--version] [--centered_data_file ] [--output_file ] [--transformed_data_file ] ``` @@ -1706,12 +1706,12 @@ An implementation of Large Margin Nearest Neighbors (LMNN), a distance learning | `--optimizer (-O)` | [`string`](#doc_string) | Optimizer to use; 'amsgrad', 'bbsgd', 'sgd', or 'lbfgs'. | `'amsgrad'` | | `--passes (-p)` | [`int`](#doc_int) | Maximum number of full passes over dataset for AMSGrad, BB_SGD and SGD. | `50` | | `--print_accuracy (-P)` | [`flag`](#doc_flag) | Print accuracies on initial and transformed dataset | | -| `--range (-R)` | [`int`](#doc_int) | Number of iterations after which impostors needs to be recalculated | `1` | | `--rank (-A)` | [`int`](#doc_int) | Rank of distance matrix to be optimized. | `0` | | `--regularization (-r)` | [`double`](#doc_double) | Regularization for LMNN objective function | `0.5` | | `--seed (-s)` | [`int`](#doc_int) | Random seed. If 0, 'std::time(NULL)' is used. | `0` | | `--step_size (-a)` | [`double`](#doc_double) | Step size for AMSGrad, BB_SGD and SGD (alpha). | `0.01` | | `--tolerance (-t)` | [`double`](#doc_double) | Maximum tolerance for termination of AMSGrad, BB_SGD, SGD or L-BFGS. | `1e-07` | +| `--update_interval (-R)` | [`int`](#doc_int) | Number of iterations after which impostors need to be recalculated. | `1` | | `--verbose (-v)` | [`flag`](#doc_flag) | Display informational messages and the full list of parameters and timers at the end of execution. | | | `--version (-V)` | [`flag`](#doc_flag) | Display the version of mlpack. Only exists in CLI binding. | | @@ -1731,7 +1731,7 @@ This program implements Large Margin Nearest Neighbors, a distance learning tech To work, this algorithm needs labeled data. It can be given as the last row of the input dataset (specified with `--input_file (-i)`), or alternatively as a separate matrix (specified with `--labels_file (-l)`). Additionally, a starting point for optimization (specified with `--distance_file (-d)`can be given, having (r x d) dimensionality. Here r should satisfy 1 <= r <= d, Consequently a Low-Rank matrix will be optimized. Alternatively, Low-Rank distance can be learned by specifying the `--rank (-A)`parameter (A Low-Rank matrix with uniformly distributed values will be used as initial learning point). -The program also requires number of targets neighbors to work with ( specified with `--k (-k)`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `--regularization (-r)`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `--range (-R)`). +The program also requires number of targets neighbors to work with ( specified with `--k (-k)`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `--regularization (-r)`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `--update_interval (-R)`). Output can either be the learned distance matrix (specified with `--output_file (-o)`), or the transformed dataset (specified with `--transformed_data_file (-D)`), or both. Additionally mean-centered dataset (specified with `--centered_data_file (-c)`) can be accessed given mean-centering (specified with `--center (-C)`) is performed on the dataset. Accuracy on initial dataset and final transformed dataset can be printed by specifying the `--print_accuracy (-P)`parameter. @@ -1755,10 +1755,10 @@ $ mlpack_lmnn --input_file iris.csv --labels_file iris_labels.csv --k 3 --optimizer bbsgd --output_file output.csv ``` -An another program call making use of range & regularization parameter with dataset having labels as last column can be made as: +Another program call making use of update interval & regularization parameter with dataset having labels as last column can be made as: ```bash -$ mlpack_lmnn --input_file letter_recognition.csv --k 5 --range 10 +$ mlpack_lmnn --input_file letter_recognition.csv --k 5 --update_interval 10 --regularization 0.4 --output_file output.csv ``` diff --git a/doc/user/bindings/go.md b/doc/user/bindings/go.md index c9baae764f..71878e2430 100644 --- a/doc/user/bindings/go.md +++ b/doc/user/bindings/go.md @@ -2069,12 +2069,12 @@ param.Normalize = false param.Optimizer = "amsgrad" param.Passes = 50 param.PrintAccuracy = false -param.Range = 1 param.Rank = 0 param.Regularization = 0.5 param.Seed = 0 param.StepSize = 0.01 param.Tolerance = 1e-07 +param.UpdateInterval = 1 param.Verbose = false centered_data, output, transformed_data := mlpack.Lmnn(input, param) @@ -2102,12 +2102,12 @@ There are two types of input options: required options, which are passed directl | `Optimizer` | [`string`](#doc_string) | Optimizer to use; 'amsgrad', 'bbsgd', 'sgd', or 'lbfgs'. | `"amsgrad"` | | `Passes` | [`int`](#doc_int) | Maximum number of full passes over dataset for AMSGrad, BB_SGD and SGD. | `50` | | `PrintAccuracy` | [`bool`](#doc_bool) | Print accuracies on initial and transformed dataset | `false` | -| `Range` | [`int`](#doc_int) | Number of iterations after which impostors needs to be recalculated | `1` | | `Rank` | [`int`](#doc_int) | Rank of distance matrix to be optimized. | `0` | | `Regularization` | [`float64`](#doc_float64) | Regularization for LMNN objective function | `0.5` | | `Seed` | [`int`](#doc_int) | Random seed. If 0, 'std::time(NULL)' is used. | `0` | | `StepSize` | [`float64`](#doc_float64) | Step size for AMSGrad, BB_SGD and SGD (alpha). | `0.01` | | `Tolerance` | [`float64`](#doc_float64) | Maximum tolerance for termination of AMSGrad, BB_SGD, SGD or L-BFGS. | `1e-07` | +| `UpdateInterval` | [`int`](#doc_int) | Number of iterations after which impostors need to be recalculated. | `1` | | `Verbose` | [`bool`](#doc_bool) | Display informational messages and the full list of parameters and timers at the end of execution. | `false` | ### Output options @@ -2127,7 +2127,7 @@ This program implements Large Margin Nearest Neighbors, a distance learning tech To work, this algorithm needs labeled data. It can be given as the last row of the input dataset (specified with `Input`), or alternatively as a separate matrix (specified with `Labels`). Additionally, a starting point for optimization (specified with `Distance`can be given, having (r x d) dimensionality. Here r should satisfy 1 <= r <= d, Consequently a Low-Rank matrix will be optimized. Alternatively, Low-Rank distance can be learned by specifying the `Rank`parameter (A Low-Rank matrix with uniformly distributed values will be used as initial learning point). -The program also requires number of targets neighbors to work with ( specified with `K`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `Regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `Range`). +The program also requires number of targets neighbors to work with ( specified with `K`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `Regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `UpdateInterval`). Output can either be the learned distance matrix (specified with `Output`), or the transformed dataset (specified with `TransformedData`), or both. Additionally mean-centered dataset (specified with `CenteredData`) can be accessed given mean-centering (specified with `Center`) is performed on the dataset. Accuracy on initial dataset and final transformed dataset can be printed by specifying the `PrintAccuracy`parameter. @@ -2156,13 +2156,13 @@ param.Optimizer = "bbsgd" _, output, _ := mlpack.Lmnn(iris, param) ``` -An another program call making use of range & regularization parameter with dataset having labels as last column can be made as: +Another program call making use of update interval & regularization parameter with dataset having labels as last column can be made as: ```go // Initialize optional parameters for Lmnn(). param := mlpack.LmnnOptions() param.K = 5 -param.Range = 10 +param.UpdateInterval = 10 param.Regularization = 0.4 _, output, _ := mlpack.Lmnn(letter_recognition, param) diff --git a/doc/user/bindings/julia.md b/doc/user/bindings/julia.md index 5d9e699354..6b81648246 100644 --- a/doc/user/bindings/julia.md +++ b/doc/user/bindings/julia.md @@ -1696,9 +1696,9 @@ julia> using mlpack: lmnn julia> centered_data, output, transformed_data = lmnn(input; batch_size=50, center=false, distance=zeros(0, 0), k=1, labels=Int[], linear_scan=false, max_iterations=100000, normalize=false, - optimizer="amsgrad", passes=50, print_accuracy=false, range=1, rank=0, + optimizer="amsgrad", passes=50, print_accuracy=false, rank=0, regularization=0.5, seed=0, step_size=0.01, tolerance=1e-07, - verbose=false) + update_interval=1, verbose=false) ``` An implementation of Large Margin Nearest Neighbors (LMNN), a distance learning technique. Given a labeled dataset, this learns a transformation of the data that improves k-nearest-neighbor performance; this can be useful as a preprocessing step. [Detailed documentation](#lmnn_detailed-documentation). @@ -1722,12 +1722,12 @@ An implementation of Large Margin Nearest Neighbors (LMNN), a distance learning | `optimizer` | [`String`](#doc_String) | Optimizer to use; 'amsgrad', 'bbsgd', 'sgd', or 'lbfgs'. | `"amsgrad"` | | `passes` | [`Int`](#doc_Int) | Maximum number of full passes over dataset for AMSGrad, BB_SGD and SGD. | `50` | | `print_accuracy` | [`Bool`](#doc_Bool) | Print accuracies on initial and transformed dataset | `false` | -| `range` | [`Int`](#doc_Int) | Number of iterations after which impostors needs to be recalculated | `1` | | `rank` | [`Int`](#doc_Int) | Rank of distance matrix to be optimized. | `0` | | `regularization` | [`Float64`](#doc_Float64) | Regularization for LMNN objective function | `0.5` | | `seed` | [`Int`](#doc_Int) | Random seed. If 0, 'std::time(NULL)' is used. | `0` | | `step_size` | [`Float64`](#doc_Float64) | Step size for AMSGrad, BB_SGD and SGD (alpha). | `0.01` | | `tolerance` | [`Float64`](#doc_Float64) | Maximum tolerance for termination of AMSGrad, BB_SGD, SGD or L-BFGS. | `1e-07` | +| `update_interval` | [`Int`](#doc_Int) | Number of iterations after which impostors need to be recalculated. | `1` | | `verbose` | [`Bool`](#doc_Bool) | Display informational messages and the full list of parameters and timers at the end of execution. | `false` | ### Output options @@ -1747,7 +1747,7 @@ This program implements Large Margin Nearest Neighbors, a distance learning tech To work, this algorithm needs labeled data. It can be given as the last row of the input dataset (specified with `input`), or alternatively as a separate matrix (specified with `labels`). Additionally, a starting point for optimization (specified with `distance`can be given, having (r x d) dimensionality. Here r should satisfy 1 <= r <= d, Consequently a Low-Rank matrix will be optimized. Alternatively, Low-Rank distance can be learned by specifying the `rank`parameter (A Low-Rank matrix with uniformly distributed values will be used as initial learning point). -The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `range`). +The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `update_interval`). Output can either be the learned distance matrix (specified with `output`), or the transformed dataset (specified with `transformed_data`), or both. Additionally mean-centered dataset (specified with `centered_data`) can be accessed given mean-centering (specified with `center`) is performed on the dataset. Accuracy on initial dataset and final transformed dataset can be printed by specifying the `print_accuracy`parameter. @@ -1774,13 +1774,13 @@ julia> _, output, _ = lmnn(iris; k=3, labels=iris_labels, optimizer="bbsgd") ``` -An another program call making use of range & regularization parameter with dataset having labels as last column can be made as: +Another program call making use of update interval & regularization parameter with dataset having labels as last column can be made as: ```julia julia> using CSV julia> letter_recognition = CSV.read("letter_recognition.csv") -julia> _, output, _ = lmnn(letter_recognition; k=5, range=10, - regularization=0.4) +julia> _, output, _ = lmnn(letter_recognition; k=5, + regularization=0.4, update_interval=10) ``` ### See also diff --git a/doc/user/bindings/python.md b/doc/user/bindings/python.md index a0a777fc72..8399b13531 100644 --- a/doc/user/bindings/python.md +++ b/doc/user/bindings/python.md @@ -1715,8 +1715,8 @@ Then, to use that model to predict classes for the dataset '`'test'`', storing t copy_all_inputs=False, distance=np.empty([0, 0]), input_=np.empty([0, 0]), k=1, labels=np.empty([0], dtype=np.uint64), linear_scan=False, max_iterations=100000, normalize=False, optimizer='amsgrad', passes=50, - print_accuracy=False, range=1, rank=0, regularization=0.5, seed=0, - step_size=0.01, tolerance=1e-07, verbose=False) + print_accuracy=False, rank=0, regularization=0.5, seed=0, + step_size=0.01, tolerance=1e-07, update_interval=1, verbose=False) >>> centered_data = d['centered_data'] >>> output = d['output'] >>> transformed_data = d['transformed_data'] @@ -1744,12 +1744,12 @@ An implementation of Large Margin Nearest Neighbors (LMNN), a distance learning | `optimizer` | [`str`](#doc_str) | Optimizer to use; 'amsgrad', 'bbsgd', 'sgd', or 'lbfgs'. | `'amsgrad'` | | `passes` | [`int`](#doc_int) | Maximum number of full passes over dataset for AMSGrad, BB_SGD and SGD. | `50` | | `print_accuracy` | [`bool`](#doc_bool) | Print accuracies on initial and transformed dataset | `False` | -| `range` | [`int`](#doc_int) | Number of iterations after which impostors needs to be recalculated | `1` | | `rank` | [`int`](#doc_int) | Rank of distance matrix to be optimized. | `0` | | `regularization` | [`float`](#doc_float) | Regularization for LMNN objective function | `0.5` | | `seed` | [`int`](#doc_int) | Random seed. If 0, 'std::time(NULL)' is used. | `0` | | `step_size` | [`float`](#doc_float) | Step size for AMSGrad, BB_SGD and SGD (alpha). | `0.01` | | `tolerance` | [`float`](#doc_float) | Maximum tolerance for termination of AMSGrad, BB_SGD, SGD or L-BFGS. | `1e-07` | +| `update_interval` | [`int`](#doc_int) | Number of iterations after which impostors need to be recalculated. | `1` | | `verbose` | [`bool`](#doc_bool) | Display informational messages and the full list of parameters and timers at the end of execution. | `False` | ### Output options @@ -1769,7 +1769,7 @@ This program implements Large Margin Nearest Neighbors, a distance learning tech To work, this algorithm needs labeled data. It can be given as the last row of the input dataset (specified with `input_`), or alternatively as a separate matrix (specified with `labels`). Additionally, a starting point for optimization (specified with `distance`can be given, having (r x d) dimensionality. Here r should satisfy 1 <= r <= d, Consequently a Low-Rank matrix will be optimized. Alternatively, Low-Rank distance can be learned by specifying the `rank`parameter (A Low-Rank matrix with uniformly distributed values will be used as initial learning point). -The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `range`). +The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `update_interval`). Output can either be the learned distance matrix (specified with `output`), or the transformed dataset (specified with `transformed_data`), or both. Additionally mean-centered dataset (specified with `centered_data`) can be accessed given mean-centering (specified with `center`) is performed on the dataset. Accuracy on initial dataset and final transformed dataset can be printed by specifying the `print_accuracy`parameter. @@ -1793,10 +1793,10 @@ Example - Let's say we want to learn distance on iris dataset with number of tar >>> output = output['output'] ``` -An another program call making use of range & regularization parameter with dataset having labels as last column can be made as: +Another program call making use of update interval & regularization parameter with dataset having labels as last column can be made as: ```python ->>> output = lmnn(input_=letter_recognition, k=5, range=10, +>>> output = lmnn(input_=letter_recognition, k=5, update_interval=10, regularization=0.4) >>> output = output['output'] ``` diff --git a/doc/user/bindings/r.md b/doc/user/bindings/r.md index 2aa0037959..b6a242b521 100644 --- a/doc/user/bindings/r.md +++ b/doc/user/bindings/r.md @@ -1689,9 +1689,9 @@ R> library(mlpack) R> d <- lmnn(batch_size=50, center=FALSE, distance=matrix(numeric(), 0, 0), input=matrix(numeric(), 0, 0), k=1, labels=matrix(integer(), 0, 0), linear_scan=FALSE, max_iterations=100000, normalize=FALSE, - optimizer="amsgrad", passes=50, print_accuracy=FALSE, range=1, rank=0, + optimizer="amsgrad", passes=50, print_accuracy=FALSE, rank=0, regularization=0.5, seed=0, step_size=0.01, tolerance=1e-07, - verbose=getOption("mlpack.verbose", FALSE)) + update_interval=1, verbose=getOption("mlpack.verbose", FALSE)) R> centered_data <- d$centered_data R> output <- d$output R> transformed_data <- d$transformed_data @@ -1718,12 +1718,12 @@ An implementation of Large Margin Nearest Neighbors (LMNN), a distance learning | `optimizer` | [`character`](#doc_character) | Optimizer to use; 'amsgrad', 'bbsgd', 'sgd', or 'lbfgs'. | `"amsgrad"` | | `passes` | [`integer`](#doc_integer) | Maximum number of full passes over dataset for AMSGrad, BB_SGD and SGD. | `50` | | `print_accuracy` | [`logical`](#doc_logical) | Print accuracies on initial and transformed dataset | `FALSE` | -| `range` | [`integer`](#doc_integer) | Number of iterations after which impostors needs to be recalculated | `1` | | `rank` | [`integer`](#doc_integer) | Rank of distance matrix to be optimized. | `0` | | `regularization` | [`numeric`](#doc_numeric) | Regularization for LMNN objective function | `0.5` | | `seed` | [`integer`](#doc_integer) | Random seed. If 0, 'std::time(NULL)' is used. | `0` | | `step_size` | [`numeric`](#doc_numeric) | Step size for AMSGrad, BB_SGD and SGD (alpha). | `0.01` | | `tolerance` | [`numeric`](#doc_numeric) | Maximum tolerance for termination of AMSGrad, BB_SGD, SGD or L-BFGS. | `1e-07` | +| `update_interval` | [`integer`](#doc_integer) | Number of iterations after which impostors need to be recalculated. | `1` | | `verbose` | [`logical`](#doc_logical) | Display informational messages and the full list of parameters and timers at the end of execution. | `getOption("mlpack.verbose", FALSE)` | ### Output options @@ -1743,7 +1743,7 @@ This program implements Large Margin Nearest Neighbors, a distance learning tech To work, this algorithm needs labeled data. It can be given as the last row of the input dataset (specified with `input`), or alternatively as a separate matrix (specified with `labels`). Additionally, a starting point for optimization (specified with `distance`can be given, having (r x d) dimensionality. Here r should satisfy 1 <= r <= d, Consequently a Low-Rank matrix will be optimized. Alternatively, Low-Rank distance can be learned by specifying the `rank`parameter (A Low-Rank matrix with uniformly distributed values will be used as initial learning point). -The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `range`). +The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `update_interval`). Output can either be the learned distance matrix (specified with `output`), or the transformed dataset (specified with `transformed_data`), or both. Additionally mean-centered dataset (specified with `centered_data`) can be accessed given mean-centering (specified with `center`) is performed on the dataset. Accuracy on initial dataset and final transformed dataset can be printed by specifying the `print_accuracy`parameter. @@ -1767,10 +1767,10 @@ R> output <- lmnn(input=iris, labels=iris_labels, k=3, optimizer="bbsgd") R> output <- output$output ``` -An another program call making use of range & regularization parameter with dataset having labels as last column can be made as: +Another program call making use of update interval & regularization parameter with dataset having labels as last column can be made as: ```R -R> output <- lmnn(input=letter_recognition, k=5, range=10, +R> output <- lmnn(input=letter_recognition, k=5, update_interval=10, regularization=0.4) R> output <- output$output ``` diff --git a/doc/user/core.md b/doc/user/core.md index 0654103a66..1917958ed2 100644 --- a/doc/user/core.md +++ b/doc/user/core.md @@ -862,9 +862,9 @@ including: * [`NeighborSearch`](/src/mlpack/methods/neighbor_search/neighbor_search.hpp) * [`RangeSearch`](/src/mlpack/methods/range_search/range_search.hpp) - * [`LMNN`](/src/mlpack/methods/lmnn/lmnn.hpp) + * [`LMNN`](user/methods/lmnn.md) * [`EMST`](/src/mlpack/methods/emst/emst.hpp) - * [`NCA`](/src/mlpack/methods/nca/nca.hpp) + * [`NCA`](user/methods/nca.md) * [`RANN`](/src/mlpack/methods/rann/rann.hpp) * [`KMeans`](/src/mlpack/methods/kmeans/kmeans.hpp) From 554ad35a4c58541c9b8bad220a646f7198402673 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 6 Jul 2024 15:31:58 -0400 Subject: [PATCH 091/212] Don't use OpenMP 5.0 support (since we don't require it). --- src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp b/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp index 227f5ace0e..01e133f338 100644 --- a/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp +++ b/src/mlpack/methods/nca/nca_softmax_error_function_impl.hpp @@ -273,7 +273,10 @@ void SoftmaxErrorFunction::Precalculate( // order of O((n * (n + 1)) / 2), which really isn't all that great. p.zeros(stretchedDataset.n_cols); denominators.zeros(stretchedDataset.n_cols); - #pragma omp parallel for collapse(2) + + // A collapse(2) would be helpful here, but appears to not be supported fully + // until OpenMP 5.0. + #pragma omp parallel for for (size_t i = 0; i < stretchedDataset.n_cols; ++i) { for (size_t j = (i + 1); j < stretchedDataset.n_cols; ++j) From c4fbd30bd4fb0efe0ea4c97549e4a2c0f813b2da Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 6 Jul 2024 17:52:38 -0400 Subject: [PATCH 092/212] Adjust some tolerances for float types. --- src/mlpack/tests/nca_test.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/mlpack/tests/nca_test.cpp b/src/mlpack/tests/nca_test.cpp index f4fd6a081e..b055d99926 100644 --- a/src/mlpack/tests/nca_test.cpp +++ b/src/mlpack/tests/nca_test.cpp @@ -41,14 +41,16 @@ TEMPLATE_TEST_CASE("SoftmaxInitialPoint", "[NCATest]", float, double) // Verify the initial point is the identity matrix. arma::Mat initialPoint = sef.GetInitialPoint(); + const double eps = std::is_same::value ? 1e-4 : 1e-7; + const double margin = std::is_same::value ? 1e-4 : 1e-5; for (int row = 0; row < 5; row++) { for (int col = 0; col < 5; col++) { if (row == col) - REQUIRE(initialPoint(row, col) == Approx(1.0).epsilon(1e-7)); + REQUIRE(initialPoint(row, col) == Approx(1.0).epsilon(eps)); else - REQUIRE(initialPoint(row, col) == Approx(0.0).margin(1e-5)); + REQUIRE(initialPoint(row, col) == Approx(0.0).margin(margin)); } } } @@ -129,7 +131,8 @@ TEMPLATE_TEST_CASE("SoftmaxOptimalEvaluation", "[NCATest]", float, double) // Use a very close tolerance for optimality; we need to be sure this function // gives optimal results correctly. - REQUIRE(objective == Approx(-4.0).epsilon(1e-12)); + const double eps = std::is_same::value ? 1e-6 : 1e-12; + REQUIRE(objective == Approx(-4.0).epsilon(eps)); } /** @@ -343,8 +346,8 @@ TEMPLATE_TEST_CASE("NCALBFGSSimpleDataset", "[NCATest]", float, double) // finalObj must be less than initObj. REQUIRE(finalObj < initObj); // Verify that final objective is optimal. - REQUIRE(finalObj == Approx(-6.0).epsilon(1e-7)); + REQUIRE(finalObj == Approx(-6.0).epsilon(0.00001)); // The solution is not unique, so the best we can do is ensure the gradient // norm is close to 0. - REQUIRE(arma::norm(finalGradient, 2) < 1e-6); + REQUIRE(arma::norm(finalGradient, 2) < 1e-5); } From 715c71f74c2534c82b6d10b99d2b6d7815f3533d Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Sun, 7 Jul 2024 01:09:40 +0200 Subject: [PATCH 093/212] Update dropout_impl.hpp --- src/mlpack/methods/ann/layer/dropout_impl.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mlpack/methods/ann/layer/dropout_impl.hpp b/src/mlpack/methods/ann/layer/dropout_impl.hpp index 8a825119a3..e0e1a13106 100644 --- a/src/mlpack/methods/ann/layer/dropout_impl.hpp +++ b/src/mlpack/methods/ann/layer/dropout_impl.hpp @@ -77,12 +77,15 @@ DropoutType::operator=(DropoutType&& other) template void DropoutType::Forward(const MatType& input, MatType& output) { + // The dropout mask will not be multiplied in testing mode. if (!this->training) { output = input; } else { + // Scale with input / (1 - ratio) and set values to zero with probability + // 'ratio'. mask.randu(input.n_rows, input.n_cols); #pragma omp parallel for collapse(2) for (size_t i = 0; i < input.n_rows; ++i) From 0a15b0872befec87ecdacc72c1257dc6046ad35b Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sun, 7 Jul 2024 11:51:16 -0400 Subject: [PATCH 094/212] Oops, should have built locally first. --- src/mlpack/tests/nca_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mlpack/tests/nca_test.cpp b/src/mlpack/tests/nca_test.cpp index b055d99926..20df35df87 100644 --- a/src/mlpack/tests/nca_test.cpp +++ b/src/mlpack/tests/nca_test.cpp @@ -41,8 +41,8 @@ TEMPLATE_TEST_CASE("SoftmaxInitialPoint", "[NCATest]", float, double) // Verify the initial point is the identity matrix. arma::Mat initialPoint = sef.GetInitialPoint(); - const double eps = std::is_same::value ? 1e-4 : 1e-7; - const double margin = std::is_same::value ? 1e-4 : 1e-5; + const double eps = std::is_same::value ? 1e-4 : 1e-7; + const double margin = std::is_same::value ? 1e-4 : 1e-5; for (int row = 0; row < 5; row++) { for (int col = 0; col < 5; col++) @@ -131,7 +131,7 @@ TEMPLATE_TEST_CASE("SoftmaxOptimalEvaluation", "[NCATest]", float, double) // Use a very close tolerance for optimality; we need to be sure this function // gives optimal results correctly. - const double eps = std::is_same::value ? 1e-6 : 1e-12; + const double eps = std::is_same::value ? 1e-6 : 1e-12; REQUIRE(objective == Approx(-4.0).epsilon(eps)); } From 990ed83f58f3d64a3817dca1c7e07521cf66dfba Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 8 Jul 2024 13:33:42 -0400 Subject: [PATCH 095/212] Add stale configuration to replace mlpack-bot. --- .github/workflows/stale.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..2e033416b9 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,23 @@ +name: Close inactive issues +on: + schedule: + - cron: "30 1 * * *" + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@9 + with: + days-before-issues-stale: 30 + days-before-issue-close: 7 + stale-issue-label: "s: stale", + stale-issue-message: "This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions! :+1:" + days-before-pr-stale: 30, + days-before-pr-close: 14 + repo-token: ${{ secrets.GITHUB_TOKEN }} + exempt-issue-labels: "s: keep open" + exempt-pr-labels: "s: keep open" From de9194cb0b98528dcf2d3f737d0d094a662f957b Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 8 Jul 2024 13:38:58 -0400 Subject: [PATCH 096/212] Add auto-approval Github action. --- .github/workflows/auto-approve.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/auto-approve.yml diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml new file mode 100644 index 0000000000..6694c27f25 --- /dev/null +++ b/.github/workflows/auto-approve.yml @@ -0,0 +1,22 @@ +# Once a PR has been approved by one member of the mlpack organization, a second +# approving review will automatically be added 24 hours later. This allows time +# for other maintainers to take a look. +name: Auto-approve pull requests +on: + schedule: + # Run roughly every four hours. + - cron: "15 0,4,8,12,16,20 * * *" + +jobs: + auto-approve: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + - name: Auto-approve pull requests + uses: rcurtin/auto-approve + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + approval-message: + 'Second approval provided automatically after 24 hours. :+1:' From 77635c304b2ba1adcc486c0c6e1647a0cac013c2 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 8 Jul 2024 13:41:13 -0400 Subject: [PATCH 097/212] No comma needed. --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 2e033416b9..9a85fd1f2b 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -14,7 +14,7 @@ jobs: with: days-before-issues-stale: 30 days-before-issue-close: 7 - stale-issue-label: "s: stale", + stale-issue-label: "s: stale" stale-issue-message: "This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions! :+1:" days-before-pr-stale: 30, days-before-pr-close: 14 From 5482085cac4026736041891fe33c5487f62b30f3 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 8 Jul 2024 13:45:44 -0400 Subject: [PATCH 098/212] Use specific version. --- .github/workflows/auto-approve.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml index 6694c27f25..0892cc3a2f 100644 --- a/.github/workflows/auto-approve.yml +++ b/.github/workflows/auto-approve.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Auto-approve pull requests - uses: rcurtin/auto-approve + uses: rcurtin/auto-approve@v1.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} approval-message: From 7aaf85f82bf9ba27dc5dd2c6a32714a21d0b501f Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 8 Jul 2024 14:13:25 -0400 Subject: [PATCH 099/212] Post about stickers as a Github action. --- .github/workflows/stickers.yaml | 17 +++++++++++++++++ .github/workflows/welcome-pr.yaml | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 .github/workflows/stickers.yaml create mode 100644 .github/workflows/welcome-pr.yaml diff --git a/.github/workflows/stickers.yaml b/.github/workflows/stickers.yaml new file mode 100644 index 0000000000..f4b2d78056 --- /dev/null +++ b/.github/workflows/stickers.yaml @@ -0,0 +1,17 @@ +# Post a message to new contributors that they can get some stickers mailed to +# them. +name: 'Stickers for new contributors' +on: + pull_request: + types: [closed] + +jobs: + sticker_comment: + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true + steps: + # Forked version of first-interaction that runs only on first merged PR. + - uses: rcurtin/first-interaction@v1.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + pr-message: "Hello there! Thanks for your contribution. Congratulations on your first contribution to mlpack! If you'd like to add your name to the list of contributors in `COPYRIGHT.txt` and you haven't already, please feel free to push a change to this PR---or, if it gets merged before you can, feel free to open another PR.\n\nIn addition, if you'd like some stickers to put on your laptop, we can get them in the mail for you. Just send an email with your physical mailing address to stickers@mlpack.org, and then one of the mlpack maintainers will put some stickers in an envelope for you. It may take a few weeks to get them, depending on your location. :+1:" diff --git a/.github/workflows/welcome-pr.yaml b/.github/workflows/welcome-pr.yaml new file mode 100644 index 0000000000..00341842fb --- /dev/null +++ b/.github/workflows/welcome-pr.yaml @@ -0,0 +1,16 @@ +# Post a message to new contributors that they can get some stickers mailed to +# them. +name: 'Welcome message for new contributors' +on: + pull_request: + types: [open] + +jobs: + sticker_comment: + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true + steps: + - uses: actions/first-interaction@v1.3.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + pr-message: "Thanks for opening your first pull request in this repository! Someone will review it when they have a chance. In the mean time, please be sure that you've handled the following things, to make the review process quicker and easier:\n\n - All code should follow the [style guide](https://github.com/mlpack/mlpack/wiki/DesignGuidelines#style-guidelines)\n - Documentation added for any new functionality\n - Tests added for any new functionality\n - Tests that are added follow the [testing guide](https://github.com/mlpack/mlpack/wiki/Testing-Guidelines)\n - Headers and license information added to the top of any new code files\n - HISTORY.md updated if the changes are big or user-facing\n - All CI checks should be passing\n\nThank you again for your contributions! :+1:" From 44361ab0023308e892741dc7e62018851cd7fac6 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 8 Jul 2024 15:11:17 -0400 Subject: [PATCH 100/212] Filter out 400 responses. --- scripts/build-docs.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh index 855d86bd89..8b1d803320 100755 --- a/scripts/build-docs.sh +++ b/scripts/build-docs.sh @@ -405,11 +405,14 @@ do echo "Checking links in $f..."; # To run checklink we have to strip out some perl stderr warnings... + # We also filter out a number of spurious bad error codes that some webservers + # seem to give, probably to prevent crawling just like this. checklink -qs \ --follow-file-links \ --suppress-broken 405 \ --suppress-broken 503 \ --suppress-broken 301 \ + --suppress-broken 400 \ -X "https://eigen.tuxfamily.org/index.php\?title=Main_Page" \ -X "https://mlpack.slack.com/" "$f" 2>&1 | grep -v 'Use of uninitialized value' > checklink_out; From 8781589ac6ee6d1338ec142692ad661003f2287c Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 8 Jul 2024 15:40:13 -0400 Subject: [PATCH 101/212] Fix links. --- doc/user/core.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/user/core.md b/doc/user/core.md index 1917958ed2..c482688d39 100644 --- a/doc/user/core.md +++ b/doc/user/core.md @@ -862,9 +862,9 @@ including: * [`NeighborSearch`](/src/mlpack/methods/neighbor_search/neighbor_search.hpp) * [`RangeSearch`](/src/mlpack/methods/range_search/range_search.hpp) - * [`LMNN`](user/methods/lmnn.md) + * [`LMNN`](methods/lmnn.md) * [`EMST`](/src/mlpack/methods/emst/emst.hpp) - * [`NCA`](user/methods/nca.md) + * [`NCA`](methods/nca.md) * [`RANN`](/src/mlpack/methods/rann/rann.hpp) * [`KMeans`](/src/mlpack/methods/kmeans/kmeans.hpp) From 30c3bd0dd47751614476776f7c067932d7572531 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Tue, 9 Jul 2024 13:41:41 +0200 Subject: [PATCH 102/212] Remove deprecated flags and use new ones Signed-off-by: Omar Shrit --- board/flags-config.cmake | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/board/flags-config.cmake b/board/flags-config.cmake index 0f661805d2..5999ab86ac 100644 --- a/board/flags-config.cmake +++ b/board/flags-config.cmake @@ -19,7 +19,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--hash-style=gnu -Wl,--build-id=none") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,norelro") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") -## Keep the following flag in comment, it will be relevant in the case of MCU's +## Keep the following flag in comment, they might be relevant in the case of MCU's #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-nmagic,-Bsymbolic -nostartfiles") set(BOARD_NAME "" CACHE STRING "Specify Board name to optimize for.") @@ -52,18 +52,15 @@ elseif(BOARD MATCHES "CORTEXA15") set(OPENBLAS_TARGET "CORTEXA15") set(OPENBLAS_BINARY "32") elseif(BOARD MATCHES "RPI3" OR BOARD MATCHES "CORTEXA53") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a53 -mfloat-abi=hard") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon-fp-armv8 -mneon-for-64bit") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a53 -ftree-vectorize") set(OPENBLAS_TARGET "CORTEXA53") set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "RPI4" OR BOARD MATCHES "CORTEXA72") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a72 -mfloat-abi=hard") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon-fp-armv8 -mneon-for-64bit") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a72 -ftree-vectorize") set(OPENBLAS_TARGET "CORTEXA72") set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "JETSONAGX" OR BOARD MATCHES "CORTEXA76") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a76 -mfloat-abi=hard") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon-fp-armv8 -mneon-for-64bit") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a76 -ftree-vectorize") set(OPENBLAS_TARGET "CORTEXA76") set(OPENBLAS_BINARY "64") elseif(BOARD MATCHES "BV") From b4512ef3c7aa30140ba75d14682f116b5d40cd67 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 9 Jul 2024 09:33:21 -0400 Subject: [PATCH 103/212] Round one of mlpack-bot action fixes. --- .github/workflows/auto-approve.yml | 2 +- .github/workflows/stale.yml | 2 +- .github/workflows/stickers.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml index 0892cc3a2f..81c58d2033 100644 --- a/.github/workflows/auto-approve.yml +++ b/.github/workflows/auto-approve.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Auto-approve pull requests - uses: rcurtin/auto-approve@v1.0.0 + uses: rcurtin/auto-approve@v1.0.1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} approval-message: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 9a85fd1f2b..60545c7cfd 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -10,7 +10,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@9 + - uses: actions/stale@v9 with: days-before-issues-stale: 30 days-before-issue-close: 7 diff --git a/.github/workflows/stickers.yaml b/.github/workflows/stickers.yaml index f4b2d78056..89ef40d37c 100644 --- a/.github/workflows/stickers.yaml +++ b/.github/workflows/stickers.yaml @@ -13,5 +13,5 @@ jobs: # Forked version of first-interaction that runs only on first merged PR. - uses: rcurtin/first-interaction@v1.0.0 with: - token: ${{ secrets.GITHUB_TOKEN }} + repo-token: ${{ secrets.GITHUB_TOKEN }} pr-message: "Hello there! Thanks for your contribution. Congratulations on your first contribution to mlpack! If you'd like to add your name to the list of contributors in `COPYRIGHT.txt` and you haven't already, please feel free to push a change to this PR---or, if it gets merged before you can, feel free to open another PR.\n\nIn addition, if you'd like some stickers to put on your laptop, we can get them in the mail for you. Just send an email with your physical mailing address to stickers@mlpack.org, and then one of the mlpack maintainers will put some stickers in an envelope for you. It may take a few weeks to get them, depending on your location. :+1:" From 4058d18b3316f0bac7caa5ba19f5778a6faf61e0 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 10 Jul 2024 13:06:27 +0200 Subject: [PATCH 104/212] opt: hamerly kmeans openmp --- .../methods/kmeans/hamerly_kmeans_impl.hpp | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index d2e11b3304..dfafedce3f 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -12,6 +12,7 @@ #ifndef MLPACK_METHODS_KMEANS_HAMERLY_KMEANS_IMPL_HPP #define MLPACK_METHODS_KMEANS_HAMERLY_KMEANS_IMPL_HPP +#include // In case it hasn't been included yet. #include "hamerly_kmeans.hpp" @@ -50,6 +51,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // Calculate minimum intra-cluster distance for each cluster. minClusterDistances.fill(DBL_MAX); + #pragma omp parallel for reduction(+:distanceCalculations) for (size_t i = 0; i < centroids.n_cols; ++i) { for (size_t j = i + 1; j < centroids.n_cols; ++j) @@ -59,13 +61,17 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, ++distanceCalculations; // Update bounds, if this intra-cluster distance is smaller. - if (dist < minClusterDistances(i)) - minClusterDistances(i) = dist; - if (dist < minClusterDistances(j)) - minClusterDistances(j) = dist; + #pragma omp critical + { + if (dist < minClusterDistances(i)) + minClusterDistances(i) = dist; + if (dist < minClusterDistances(j)) + minClusterDistances(j) = dist; + } } } + #pragma omp parallel for reduction(+:distanceCalculations, hamerlyPruned) for (size_t i = 0; i < dataset.n_cols; ++i) { const double m = std::max(minClusterDistances(assignments[i]), @@ -74,9 +80,13 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // First bound test. if (upperBounds(i) <= m) { + #pragma omp atomic ++hamerlyPruned; - newCentroids.col(assignments[i]) += dataset.col(i); - ++counts(assignments[i]); + #pragma omp critical + { + newCentroids.col(assignments[i]) += dataset.col(i); + ++counts(assignments[i]); + } continue; } @@ -88,12 +98,15 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // Second bound test. if (upperBounds(i) <= m) { - newCentroids.col(assignments[i]) += dataset.col(i); - ++counts(assignments[i]); + #pragma omp critical + { + newCentroids.col(assignments[i]) += dataset.col(i); + ++counts(assignments[i]); + } continue; } - // The bounds failed. So test against all other clusters. + // The bounds failed. So test against all other clusters. // This is Hamerly's Point-All-Ctrs() function from the paper. // We have to reset the lower bound first. lowerBounds(i) = DBL_MAX; @@ -104,7 +117,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, const double dist = distance.Evaluate(dataset.col(i), centroids.col(c)); - // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)). + // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)). if (dist < upperBounds(i)) { // lowerBounds holds the second closest cluster. @@ -121,8 +134,11 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, distanceCalculations += centroids.n_cols - 1; // Update new centroids. - newCentroids.col(assignments[i]) += dataset.col(i); - ++counts(assignments[i]); + #pragma omp critical + { + newCentroids.col(assignments[i]) += dataset.col(i); + ++counts(assignments[i]); + } } // Normalize centroids and calculate cluster movement (contains parts of @@ -132,6 +148,9 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, size_t furthestMovingCluster = 0; arma::vec centroidMovements(centroids.n_cols); double centroidMovement = 0.0; + + #pragma omp parallel for reduction(+:distanceCalculations, centroidMovement) \ + reduction(max:furthestMovement, secondFurthestMovement) for (size_t c = 0; c < centroids.n_cols; ++c) { if (counts(c) > 0) @@ -157,6 +176,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, } // Now update bounds (lines 3-8 of Update-Bounds()). + #pragma omp parallel for for (size_t i = 0; i < dataset.n_cols; ++i) { upperBounds(i) += centroidMovements(assignments[i]); @@ -171,6 +191,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, return std::sqrt(centroidMovement); } + } // namespace mlpack #endif From e8b669f9ce3480f37a40ae03d2819747abda5047 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 10 Jul 2024 13:08:04 +0200 Subject: [PATCH 105/212] remove break --- src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index dfafedce3f..a0992ddced 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -191,7 +191,6 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, return std::sqrt(centroidMovement); } - } // namespace mlpack #endif From 8fc3f9a206eb2688039c7cc0880f1dc48fe7861b Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 10 Jul 2024 14:28:08 +0200 Subject: [PATCH 106/212] opt: openMP naive kmeans --- .../methods/kmeans/naive_kmeans_impl.hpp | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 17a9ff58bd..3224008be7 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -16,7 +16,6 @@ #ifndef MLPACK_METHODS_KMEANS_NAIVE_KMEANS_IMPL_HPP #define MLPACK_METHODS_KMEANS_NAIVE_KMEANS_IMPL_HPP -// In case it hasn't been included yet. #include "naive_kmeans.hpp" namespace mlpack { @@ -43,11 +42,10 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, #pragma omp parallel { // The current state of the K-means is private for each thread - arma::mat localCentroids(centroids.n_rows, centroids.n_cols, - arma::fill::zeros); + arma::mat localCentroids(centroids.n_rows, centroids.n_cols, arma::fill::zeros); arma::Col localCounts(centroids.n_cols, arma::fill::zeros); - #pragma omp for + #pragma omp for schedule(dynamic) for (size_t i = 0; i < (size_t) dataset.n_cols; ++i) { // Find the closest centroid to this point. @@ -56,8 +54,7 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, for (size_t j = 0; j < centroids.n_cols; ++j) { - const double dist = distance.Evaluate(dataset.col(i), - centroids.unsafe_col(j)); + const double dist = distance.Evaluate(dataset.col(i), centroids.unsafe_col(j)); if (dist < minDistance) { minDistance = dist; @@ -71,7 +68,8 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, localCentroids.unsafe_col(closestCluster) += dataset.col(i); localCounts(closestCluster)++; } - // Combine calculated state from each thread + + // Combine calculated state from each thread using atomic operations #pragma omp critical { newCentroids += localCentroids; @@ -79,7 +77,8 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, } } - // Now normalize the centroid. + // Now normalize the centroids. + #pragma omp parallel for for (size_t i = 0; i < centroids.n_cols; ++i) if (counts(i) != 0) newCentroids.col(i) /= counts(i); @@ -88,10 +87,10 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, // Calculate cluster distortion for this iteration. double cNorm = 0.0; + #pragma omp parallel for reduction(+:cNorm) for (size_t i = 0; i < centroids.n_cols; ++i) { - cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), - 2.0); + cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), 2.0); } distanceCalculations += centroids.n_cols; @@ -100,4 +99,4 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, } // namespace mlpack -#endif +#endif \ No newline at end of file From 1eb6304979c4dd01c75cee4b90065cabe0c69b52 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 10 Jul 2024 14:34:22 +0200 Subject: [PATCH 107/212] style --- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 3224008be7..d3aee88025 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -16,6 +16,7 @@ #ifndef MLPACK_METHODS_KMEANS_NAIVE_KMEANS_IMPL_HPP #define MLPACK_METHODS_KMEANS_NAIVE_KMEANS_IMPL_HPP +// In case it hasn't been included yet. #include "naive_kmeans.hpp" namespace mlpack { @@ -42,7 +43,8 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, #pragma omp parallel { // The current state of the K-means is private for each thread - arma::mat localCentroids(centroids.n_rows, centroids.n_cols, arma::fill::zeros); + arma::mat localCentroids(centroids.n_rows, centroids.n_cols, + arma::fill::zeros); arma::Col localCounts(centroids.n_cols, arma::fill::zeros); #pragma omp for schedule(dynamic) @@ -54,7 +56,8 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, for (size_t j = 0; j < centroids.n_cols; ++j) { - const double dist = distance.Evaluate(dataset.col(i), centroids.unsafe_col(j)); + const double dist = distance.Evaluate(dataset.col(i), + centroids.unsafe_col(j)); if (dist < minDistance) { minDistance = dist; @@ -68,7 +71,6 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, localCentroids.unsafe_col(closestCluster) += dataset.col(i); localCounts(closestCluster)++; } - // Combine calculated state from each thread using atomic operations #pragma omp critical { @@ -90,7 +92,8 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, #pragma omp parallel for reduction(+:cNorm) for (size_t i = 0; i < centroids.n_cols; ++i) { - cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), 2.0); + cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), + 2.0); } distanceCalculations += centroids.n_cols; From 76f491f6d1adae77ed5381e68bb51d18e9c985d6 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 10 Jul 2024 14:37:24 +0200 Subject: [PATCH 108/212] space --- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index d3aee88025..fa981e806b 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -56,7 +56,7 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, for (size_t j = 0; j < centroids.n_cols; ++j) { - const double dist = distance.Evaluate(dataset.col(i), + const double dist = distance.Evaluate(dataset.col(i), centroids.unsafe_col(j)); if (dist < minDistance) { From bce3be63b74c3a6c311fcc932ee90cc36b6b13f7 Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:45:06 +0200 Subject: [PATCH 109/212] remove #include openmp --- src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index a0992ddced..cbf5d7857c 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -12,7 +12,6 @@ #ifndef MLPACK_METHODS_KMEANS_HAMERLY_KMEANS_IMPL_HPP #define MLPACK_METHODS_KMEANS_HAMERLY_KMEANS_IMPL_HPP -#include // In case it hasn't been included yet. #include "hamerly_kmeans.hpp" From c669093fb5e215b1aec3c1721dcc6ff58a02ddfa Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 10 Jul 2024 09:04:16 -0400 Subject: [PATCH 110/212] Try to fix extra auto-approval and incorrect stickers detection. --- .github/workflows/auto-approve.yml | 2 +- .github/workflows/stickers.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml index 81c58d2033..0e4c4b81a3 100644 --- a/.github/workflows/auto-approve.yml +++ b/.github/workflows/auto-approve.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Auto-approve pull requests - uses: rcurtin/auto-approve@v1.0.1 + uses: rcurtin/auto-approve@v1.0.2 with: repo-token: ${{ secrets.GITHUB_TOKEN }} approval-message: diff --git a/.github/workflows/stickers.yaml b/.github/workflows/stickers.yaml index 89ef40d37c..bfaa763f25 100644 --- a/.github/workflows/stickers.yaml +++ b/.github/workflows/stickers.yaml @@ -11,7 +11,7 @@ jobs: if: github.event.pull_request.merged == true steps: # Forked version of first-interaction that runs only on first merged PR. - - uses: rcurtin/first-interaction@v1.0.0 + - uses: rcurtin/first-interaction@v1.0.1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} pr-message: "Hello there! Thanks for your contribution. Congratulations on your first contribution to mlpack! If you'd like to add your name to the list of contributors in `COPYRIGHT.txt` and you haven't already, please feel free to push a change to this PR---or, if it gets merged before you can, feel free to open another PR.\n\nIn addition, if you'd like some stickers to put on your laptop, we can get them in the mail for you. Just send an email with your physical mailing address to stickers@mlpack.org, and then one of the mlpack maintainers will put some stickers in an envelope for you. It may take a few weeks to get them, depending on your location. :+1:" From bce4749499a5bc5959d0754f0b06a150183e8139 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 10 Jul 2024 15:59:36 -0400 Subject: [PATCH 111/212] Update MeanShift to be more flexible in its template parameters. --- src/mlpack/methods/mean_shift/mean_shift.hpp | 25 ++-- .../methods/mean_shift/mean_shift_impl.hpp | 113 ++++++++++-------- src/mlpack/tests/mean_shift_test.cpp | 106 ++++++++-------- 3 files changed, 133 insertions(+), 111 deletions(-) diff --git a/src/mlpack/methods/mean_shift/mean_shift.hpp b/src/mlpack/methods/mean_shift/mean_shift.hpp index 8defa1c990..8362936241 100644 --- a/src/mlpack/methods/mean_shift/mean_shift.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift.hpp @@ -41,8 +41,7 @@ namespace mlpack { * @tparam MatType The type of matrix the data is stored in. */ template + typename KernelType = GaussianKernel> class MeanShift { public: @@ -67,7 +66,9 @@ class MeanShift * @param data Dataset for estimation. * @param ratio Percentage of dataset to use for nearest neighbor search. */ - double EstimateRadius(const MatType& data, const double ratio = 0.2); + template + typename MatType::elem_type EstimateRadius(const MatType& data, + const double ratio = 0.2); /** * Perform mean shift clustering on the data, returning a list of cluster @@ -81,9 +82,10 @@ class MeanShift * converge regardless of maxIterations. * @param useSeeds Set true to use seeds. */ + template void Cluster(const MatType& data, - arma::Row& assignments, - arma::mat& centroids, + LabelsType& assignments, + MatType& centroids, bool forceConvergence = true, bool useSeeds = true); @@ -116,6 +118,7 @@ class MeanShift * @param minFreq Minimum number of points in bin. * @param seed Matrix to store generated seeds in. */ + template void GenSeeds(const MatType& data, const double binSize, const int minFreq, @@ -129,12 +132,12 @@ class MeanShift * @param distances Distances to neighbors # @param centroid Store calculated centroid */ - template + template typename std::enable_if::type CalculateCentroid(const MatType& data, const std::vector& neighbors, - const std::vector& distances, - arma::colvec& centroid); + const std::vector& distances, + VecType& centroid); /** * Use mean to calculate new centroid given dataset and valid neighbors. @@ -144,12 +147,12 @@ class MeanShift * @param distances Distances to neighbors # @param centroid Store calculated centroid */ - template + template typename std::enable_if::type CalculateCentroid(const MatType& data, const std::vector& neighbors, - const std::vector&, /*unused*/ - arma::colvec& centroid); + const std::vector&, /*unused*/ + VecType& centroid); /** * If distance of two centroids is less than radius, one will be removed. diff --git a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp index b4eaf63107..1ca15d026c 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp @@ -28,11 +28,10 @@ namespace mlpack { /** * Construct the Mean Shift object. */ -template -MeanShift:: -MeanShift(const double radius, - const size_t maxIterations, - const KernelType kernel) : +template +MeanShift::MeanShift(const double radius, + const size_t maxIterations, + const KernelType kernel) : radius(radius), maxIterations(maxIterations), kernel(kernel) @@ -40,18 +39,21 @@ MeanShift(const double radius, // Nothing to do. } -template -void MeanShift::Radius(double radius) +template +void MeanShift::Radius(double radius) { this->radius = radius; } // Estimate radius based on given dataset. -template -double MeanShift:: -EstimateRadius(const MatType& data, double ratio) +template +template +typename MatType::elem_type +MeanShift::EstimateRadius(const MatType& data, + double ratio) { - KNN neighborSearch(data); + NeighborSearch + neighborSearch(data); /** * For each point in dataset, select nNeighbors nearest points and get @@ -60,14 +62,11 @@ EstimateRadius(const MatType& data, double ratio) */ const size_t nNeighbors = size_t(data.n_cols * ratio); arma::Mat neighbors; - arma::mat distances; + MatType distances; neighborSearch.Search(nNeighbors, neighbors, distances); - // Get max distance for each point. - arma::rowvec maxDistances = max(distances); - // Calculate and return the radius. - return sum(maxDistances) / (double) data.n_cols; + return sum(max(distances)) / (typename MatType::elem_type) data.n_cols; } // Class to compare two vectors. @@ -88,14 +87,14 @@ class less }; // Generate seeds from given data set. -template -void MeanShift::GenSeeds( - const MatType& data, - const double binSize, - const int minFreq, - MatType& seeds) +template +template +void MeanShift::GenSeeds(const MatType& data, + const double binSize, + const int minFreq, + MatType& seeds) { - typedef arma::colvec VecType; + typedef typename GetColType::type VecType; std::map > allSeeds; for (size_t i = 0; i < data.n_cols; ++i) { @@ -108,7 +107,7 @@ void MeanShift::GenSeeds( // Remove seeds with too few points. First we count the number of seeds we // end up with, then we add them. - std::map >::iterator it; + typename std::map >::iterator it; size_t count = 0; for (it = allSeeds.begin(); it != allSeeds.end(); ++it) if (it->second >= minFreq) @@ -129,22 +128,24 @@ void MeanShift::GenSeeds( } // Calculate new centroid with given kernel. -template -template +template +template typename std::enable_if::type -MeanShift:: -CalculateCentroid(const MatType& data, - const std::vector& neighbors, - const std::vector& distances, - arma::colvec& centroid) +MeanShift::CalculateCentroid( + const MatType& data, + const std::vector& neighbors, + const std::vector& distances, + VecType& centroid) { - double sumWeight = 0; + typedef typename MatType::elem_type ElemType; + + ElemType sumWeight = 0; for (size_t i = 0; i < neighbors.size(); ++i) { if (distances[i] > 0) { - double dist = distances[i] / radius; - double weight = kernel.Gradient(dist) / dist; + ElemType dist = distances[i] / radius; + ElemType weight = kernel.Gradient(dist) / dist; sumWeight += weight; centroid += weight * data.unsafe_col(neighbors[i]); } @@ -159,14 +160,14 @@ CalculateCentroid(const MatType& data, } // Calculate new centroid by mean. -template -template +template +template typename std::enable_if::type -MeanShift:: -CalculateCentroid(const MatType& data, - const std::vector& neighbors, - const std::vector&, /*unused*/ - arma::colvec& centroid) +MeanShift::CalculateCentroid( + const MatType& data, + const std::vector& neighbors, + const std::vector&, /*unused*/ + VecType& centroid) { for (size_t i = 0; i < neighbors.size(); ++i) centroid += data.unsafe_col(neighbors[i]); @@ -179,14 +180,19 @@ CalculateCentroid(const MatType& data, * Perform Mean Shift clustering on the data set, returning a list of cluster * assignments and centroids. */ -template -inline void MeanShift::Cluster( +template +template +inline void MeanShift::Cluster( const MatType& data, - arma::Row& assignments, - arma::mat& centroids, + LabelsType& assignments, + MatType& centroids, bool forceConvergence, bool useSeeds) { + // Convenience typedefs. + typedef typename MatType::elem_type ElemType; + typedef typename GetColType::type VecType; + if (radius <= 0) { // An invalid radius is given; an estimation is needed. @@ -202,14 +208,14 @@ inline void MeanShift::Cluster( } // Holds all centroids before removing duplicate ones. - arma::mat allCentroids(pSeeds->n_rows, pSeeds->n_cols); + MatType allCentroids(pSeeds->n_rows, pSeeds->n_cols); assignments.set_size(data.n_cols); - RangeSearch<> rangeSearcher(data); - Range validRadius(0, radius); + RangeSearch rangeSearcher(data); + RangeType validRadius((ElemType) 0, (ElemType) radius); std::vector > neighbors; - std::vector > distances; + std::vector > distances; // For each seed, perform mean shift algorithm. for (size_t i = 0; i < pSeeds->n_cols; ++i) @@ -220,7 +226,7 @@ inline void MeanShift::Cluster( || forceConvergence; completedIterations++) { // Store new centroid in this. - arma::colvec newCentroid = zeros(pSeeds->n_rows); + VecType newCentroid = zeros(pSeeds->n_rows); rangeSearcher.Search(allCentroids.unsafe_col(i), validRadius, neighbors, distances); @@ -239,7 +245,7 @@ inline void MeanShift::Cluster( bool isDuplicated = false; for (size_t k = 0; k < centroids.n_cols; ++k) { - const double distance = EuclideanDistance::Evaluate( + const ElemType distance = EuclideanDistance::Evaluate( allCentroids.unsafe_col(i), centroids.unsafe_col(k)); if (distance < radius) { @@ -285,8 +291,9 @@ inline void MeanShift::Cluster( else { // Assign centroids to each point. - KNN neighborSearcher(centroids); - arma::mat neighborDistances; + NeighborSearch + neighborSearcher(centroids); + MatType neighborDistances; arma::Mat resultingNeighbors; neighborSearcher.Search(data, 1, resultingNeighbors, neighborDistances); assignments = resultingNeighbors; diff --git a/src/mlpack/tests/mean_shift_test.cpp b/src/mlpack/tests/mean_shift_test.cpp index 1bd1fb3001..52a52c548a 100644 --- a/src/mlpack/tests/mean_shift_test.cpp +++ b/src/mlpack/tests/mean_shift_test.cpp @@ -18,48 +18,54 @@ using namespace mlpack; // Generate dataset; written transposed because it's easier to read. -arma::mat meanShiftData(" 0.0 0.0;" // Class 1. - " 0.3 0.4;" - " 0.1 0.0;" - " 0.1 0.3;" - " -0.2 -0.2;" - " -0.1 0.3;" - " -0.4 0.1;" - " 0.2 -0.1;" - " 0.3 0.0;" - " -0.3 -0.3;" - " 0.1 -0.1;" - " 0.2 -0.3;" - " -0.3 0.2;" - " 10.0 10.0;" // Class 2. - " 10.1 9.9;" - " 9.9 10.0;" - " 10.2 9.7;" - " 10.2 9.8;" - " 9.7 10.3;" - " 9.9 10.1;" - "-10.0 5.0;" // Class 3. - " -9.8 5.1;" - " -9.9 4.9;" - "-10.0 4.9;" - "-10.2 5.2;" - "-10.1 5.1;" - "-10.3 5.3;" - "-10.0 4.8;" - " -9.6 5.0;" - " -9.8 5.1;"); - +template +MatType GetMeanShiftData() +{ + return MatType(" 0.0 0.0;" // Class 1. + " 0.3 0.4;" + " 0.1 0.0;" + " 0.1 0.3;" + " -0.2 -0.2;" + " -0.1 0.3;" + " -0.4 0.1;" + " 0.2 -0.1;" + " 0.3 0.0;" + " -0.3 -0.3;" + " 0.1 -0.1;" + " 0.2 -0.3;" + " -0.3 0.2;" + " 10.0 10.0;" // Class 2. + " 10.1 9.9;" + " 9.9 10.0;" + " 10.2 9.7;" + " 10.2 9.8;" + " 9.7 10.3;" + " 9.9 10.1;" + "-10.0 5.0;" // Class 3. + " -9.8 5.1;" + " -9.9 4.9;" + "-10.0 4.9;" + "-10.2 5.2;" + "-10.1 5.1;" + "-10.3 5.3;" + "-10.0 4.8;" + " -9.6 5.0;" + " -9.8 5.1;").t(); +} /** * 30-point 3-class test case for Mean Shift. */ -TEST_CASE("MeanShiftSimpleTest", "[MeanShiftTest]") +TEMPLATE_TEST_CASE("MeanShiftSimpleTest", "[MeanShiftTest]", float, double) { + typedef TestType ElemType; + MeanShift<> meanShift; arma::Row assignments; - arma::mat centroids; - meanShift.Cluster((arma::mat) trans(meanShiftData), assignments, centroids); + arma::Mat centroids; + meanShift.Cluster(GetMeanShiftData>(), assignments, + centroids); // Now make sure we got it all right. There is no restriction on how the // clusters are ordered, so we have to be careful about that. @@ -88,8 +94,10 @@ TEST_CASE("MeanShiftSimpleTest", "[MeanShiftTest]") // Generate samples from four Gaussians, and make sure mean shift nearly // recovers those four centers. -TEST_CASE("GaussianClustering", "[MeanShiftTest]") +TEMPLATE_TEST_CASE("GaussianClustering", "[MeanShiftTest]", float, double) { + typedef TestType ElemType; + GaussianDistribution g1("0.0 0.0 0.0", arma::eye(3, 3)); GaussianDistribution g2("5.0 5.0 5.0", 2 * arma::eye(3, 3)); GaussianDistribution g3("-3.0 3.0 -1.0", arma::eye(3, 3)); @@ -100,21 +108,21 @@ TEST_CASE("GaussianClustering", "[MeanShiftTest]") bool success = false; for (size_t trial = 0; trial < 4; ++trial) { - arma::mat dataset(3, 4000); + arma::Mat dataset(3, 4000); for (size_t i = 0; i < 1000; ++i) - dataset.col(i) = g1.Random(); + dataset.col(i) = arma::conv_to>::from(g1.Random()); for (size_t i = 1000; i < 2000; ++i) - dataset.col(i) = g2.Random(); + dataset.col(i) = arma::conv_to>::from(g2.Random()); for (size_t i = 2000; i < 3000; ++i) - dataset.col(i) = g3.Random(); + dataset.col(i) = arma::conv_to>::from(g3.Random()); for (size_t i = 3000; i < 4000; ++i) - dataset.col(i) = g4.Random(); + dataset.col(i) = arma::conv_to>::from(g4.Random()); // Now that the dataset is generated, run mean shift. Pre-set radius. MeanShift<> meanShift(2.9); arma::Row assignments; - arma::mat centroids; + arma::Mat centroids; meanShift.Cluster(dataset, assignments, centroids); success = (centroids.n_cols == 4); @@ -125,21 +133,25 @@ TEST_CASE("GaussianClustering", "[MeanShiftTest]") continue; // Check that each centroid is close to only one mean. - arma::vec centroidDistances(4); + arma::Col centroidDistances(4); arma::uvec minIndices(4); for (size_t i = 0; i < 4; ++i) { - centroidDistances(0) = EuclideanDistance::Evaluate(g1.Mean(), + centroidDistances(0) = EuclideanDistance::Evaluate( + arma::conv_to>::from(g1.Mean()), centroids.col(i)); - centroidDistances(1) = EuclideanDistance::Evaluate(g2.Mean(), + centroidDistances(1) = EuclideanDistance::Evaluate( + arma::conv_to>::from(g2.Mean()), centroids.col(i)); - centroidDistances(2) = EuclideanDistance::Evaluate(g3.Mean(), + centroidDistances(2) = EuclideanDistance::Evaluate( + arma::conv_to>::from(g3.Mean()), centroids.col(i)); - centroidDistances(3) = EuclideanDistance::Evaluate(g4.Mean(), + centroidDistances(3) = EuclideanDistance::Evaluate( + arma::conv_to>::from(g4.Mean()), centroids.col(i)); // Are we near a centroid of a Gaussian? - const double minVal = centroidDistances.min(minIndices[i]); + const ElemType minVal = centroidDistances.min(minIndices[i]); success = (std::abs(minVal) <= 0.65); if (!success) break; From 8ca66b4f407528cca24cafe874bcb7609fcfb98c Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 10 Jul 2024 15:59:57 -0400 Subject: [PATCH 112/212] Add first partial draft of MeanShift documentation. --- doc/user/methods/mean_shift.md | 138 +++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 doc/user/methods/mean_shift.md diff --git a/doc/user/methods/mean_shift.md b/doc/user/methods/mean_shift.md new file mode 100644 index 0000000000..9f0d1b504b --- /dev/null +++ b/doc/user/methods/mean_shift.md @@ -0,0 +1,138 @@ +## `MeanShift` + +The `MeanShift` class implements mean shift, a clustering technique. Mean shift +models the density of the data using a specified kernel function, producing a +number of clusters that represent the data density. Mean shift does not require +the user to guess the number of clusters, and does not make any assumptions on +the shape of the data. + +mlpack's `MeanShift` class allows control of the kernel function used via +template parameters. + +#### Simple usage example: + +```c++ +// Use mean shift to cluster random data and print the number of points that +// fall into each cluster. + +// All data is uniform random 10-dimensional; replace with a data::Load() call +// or similar for a real application. +arma::mat dataset(10, 1000, arma::fill::randu); + +mlpack::MeanShift ms; // Step 1: create object. +arma::Row assignments; +arma::mat centroids; +ms.Cluster(dataset, assignments, centroids); // Step 2: perform clustering. + +// Print the number of clusters. +std::cout << "Found " << centroids.n_cols << " centroids." << std::endl; + +// Print the number of points in each cluster. +for (size_t c = 0; c < centroids.n_cols; ++c) +{ + std::cout << " * Cluster " << c << " has " << arma::accu(assignments == c) + << " points." << std::endl; +} +``` +

    More examples...

    + +#### Quick links: + + * [Constructors](#constructors): create `MeanShift` objects. + * [`Cluster()`](#cluster): perform clustering. + * [Other functionality](#other-functionality) for loading, saving, inspecting, + and estimating the radius to use. + * [Examples](#simple-examples) of simple usage and links to detailed example + projects. + * [Template parameters](#advanced-functionality-template-parameters) for custom + behavior. + +#### See also: + + * [mlpack clustering algorithms](../../index.md#clustering-algorithms) + * [mlpack kernels](../core.md#kernels) + * [Mean shift on Wikipedia](https://en.wikipedia.org/wiki/Mean_shift) + * [Mean Shift, Mode Seeking, and Clustering (pdf)](http://users.isr.ist.utl.pt/~alex/Resources/meanshift.pdf) + +### Constructors + + * `ms = MeanShift(radius=0, maxIterations=1000)` + +--- + + * `ms = MeanShift(radius=0, maxIterations=1000)` + +--- + + * `ms = MeanShift(radius, maxIterations, kernel)` + +--- + + * `ms = MeanShift(radius, maxIterations, kernel)` + +--- + +### Clustering + + * `ms.Cluster(data, centroids, forceConvergence=true, useSeeds=true)` + +--- + + * `ms.Cluster(data, assignments, centroids, forceConvergence=true, useSeeds=true)` + +--- + +### Other Functionality + + * A `MeanShift` object can be serialized with + [`data::Save()` and `data::Load()`](../load_save.md#mlpack-objects). + + * `EstimateRadius()` + +### Simple Examples + +Perform mean shift clustering on the satellite dataset and print the average +distance from each point to its assigned centroid. + +```c++ + +``` + +--- + +Perform mean shift clustering with custom settings of `radius` and +`maxIterations` on a subset of the covertype dataset, using `EstimateRadius()` +to set the initial radius. + +```c++ +``` + +--- + +Perform mean shift clustering with no kernel (e.g. unit weighting of points in a +centroid) on the cloud dataset. + +```c++ + +``` + +--- + +Perform mean shift clustering with the triangular kernel on the cloud dataset, +using 32-bit floating point matrices to represent the data. + +```c++ + +``` + +--- + +Perform mean shift clustering on a random sparse dataset. + +```c++ + +``` + +--- + +### Advanced Functionality: Template Parameters From 48162f2913baee09593e307398f6f117d0f544ac Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Thu, 11 Jul 2024 10:56:17 +0200 Subject: [PATCH 113/212] Adding an else to make the condition better Signed-off-by: Omar Shrit --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43bf312d01..5ec830d509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -252,8 +252,9 @@ else() if (NOT CMAKE_CROSSCOMPILING) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O3") + else() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") endif() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") else () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O3") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /O3") From 62017342f67c2e90591c139d712a17ccc222b77a Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Thu, 11 Jul 2024 12:49:00 +0200 Subject: [PATCH 114/212] Remove the restriction on CheckAtomic in the case of crosscompilation Signed-off-by: Omar Shrit --- CMake/CheckAtomic.cmake | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CMake/CheckAtomic.cmake b/CMake/CheckAtomic.cmake index 145518d4a3..3985c9aa0a 100644 --- a/CMake/CheckAtomic.cmake +++ b/CMake/CheckAtomic.cmake @@ -70,11 +70,9 @@ if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64) if(HAVE_CXX_LIBATOMICS64) list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") - if (NOT CMAKE_CROSSCOMPILING) - check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) - if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) - message(FATAL_ERROR "Host compiler must support std::atomic!") - endif() + check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) + if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) + message(FATAL_ERROR "Host compiler must support std::atomic!") endif() else() message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.") From fbb32af549c25576b704885feb1d200ef524e220 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 11 Jul 2024 21:11:35 -0400 Subject: [PATCH 115/212] Move FirstElementIsArma into core utilities. --- src/mlpack/core.hpp | 1 + .../{methods/nca => core/util}/first_element_is_arma.hpp | 6 +++--- src/mlpack/methods/lmnn/lmnn.hpp | 1 - src/mlpack/methods/nca/nca.hpp | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) rename src/mlpack/{methods/nca => core/util}/first_element_is_arma.hpp (86%) diff --git a/src/mlpack/core.hpp b/src/mlpack/core.hpp index 73eeaaece4..7205587ab5 100644 --- a/src/mlpack/core.hpp +++ b/src/mlpack/core.hpp @@ -37,6 +37,7 @@ // Now the core mlpack classes. #include #include +#include #include #include #include diff --git a/src/mlpack/methods/nca/first_element_is_arma.hpp b/src/mlpack/core/util/first_element_is_arma.hpp similarity index 86% rename from src/mlpack/methods/nca/first_element_is_arma.hpp rename to src/mlpack/core/util/first_element_is_arma.hpp index e82af9c152..60adcc0fe3 100644 --- a/src/mlpack/methods/nca/first_element_is_arma.hpp +++ b/src/mlpack/core/util/first_element_is_arma.hpp @@ -1,12 +1,12 @@ /** - * @file methods/nca/first_element_is_arma.hpp + * @file core/util/first_element_is_arma.hpp * @author Ryan Curtin * * Utility struct to detect whether the first element in a parameter pack is an * Armadillo type. */ -#ifndef MLPACK_METHODS_NCA_FIRST_ELEMENT_IS_ARMA_HPP -#define MLPACK_METHODS_NCA_FIRST_ELEMENT_IS_ARMA_HPP +#ifndef MLPACK_CORE_UTIL_FIRST_ELEMENT_IS_ARMA_HPP +#define MLPACK_CORE_UTIL_FIRST_ELEMENT_IS_ARMA_HPP #include diff --git a/src/mlpack/methods/lmnn/lmnn.hpp b/src/mlpack/methods/lmnn/lmnn.hpp index 786cbcf924..8dadc5b31e 100644 --- a/src/mlpack/methods/lmnn/lmnn.hpp +++ b/src/mlpack/methods/lmnn/lmnn.hpp @@ -14,7 +14,6 @@ #include -#include "../nca/first_element_is_arma.hpp" #include "constraints.hpp" #include "lmnn_function.hpp" diff --git a/src/mlpack/methods/nca/nca.hpp b/src/mlpack/methods/nca/nca.hpp index 2d662e2fd3..fd66154b28 100644 --- a/src/mlpack/methods/nca/nca.hpp +++ b/src/mlpack/methods/nca/nca.hpp @@ -15,7 +15,6 @@ #include #include "nca_softmax_error_function.hpp" -#include "first_element_is_arma.hpp" namespace mlpack { From ce4aef7ef7543f9c8dddee530f35cdbf2d2fbf93 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 12 Jul 2024 10:07:26 -0400 Subject: [PATCH 116/212] Fix environment variables in CI jobs. --- .ci/ci.yaml | 24 ++++++++++++++---------- .ci/linux-steps.yaml | 17 ++++++++--------- .ci/macos-steps.yaml | 11 +++++------ 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/.ci/ci.yaml b/.ci/ci.yaml index 7a1f41de0b..1805f91c59 100644 --- a/.ci/ci.yaml +++ b/.ci/ci.yaml @@ -19,16 +19,18 @@ jobs: # RAM usage. CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF' Python: - binding: 'python' - python.version: '3.7' CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=ON -DPYTHON_EXECUTABLE=/usr/bin/python3 -DBUILD_GO_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF' + python.version: '3.7' + env: + BINDING: 'python' Julia: - julia.version: '1.3.0' CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=ON -DBUILD_GO_BINDINGS=OFF -DJULIA_EXECUTABLE=/opt/julia-1.6.3/bin/julia -DBUILD_R_BINDINGS=OFF' + env: + BINDING: 'julia' Go: - binding: 'go' - go.version: '1.11.0' CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=ON -DBUILD_R_BINDINGS=OFF' + env: + BINDING: 'go' Markdown: CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_MARKDOWN_BINDINGS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF' @@ -44,21 +46,23 @@ jobs: # clang on OS X segfaults when using precompiled headers, so we disable # them. Plain: - CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF' python.version: '3.8' + CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF' Python: - binding: 'python' python.version: '3.8' CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=ON -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF' + env: + BINDING: 'python' Julia: python.version: '3.8' - julia.version: '1.6.3' CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_JULIA_BINDINGS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF' + env: + BINDING: 'julia' Go: - binding: 'go' python.version: '3.8' - go.version: '1.11.0' CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=ON -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF' + env: + BINDING: 'go' steps: - template: macos-steps.yaml diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml index ebaa342552..9b288d76ed 100644 --- a/.ci/linux-steps.yaml +++ b/.ci/linux-steps.yaml @@ -7,7 +7,7 @@ steps: # Set python version - task: UsePythonVersion@0 inputs: - versionSpec: '3.7' + versionSpec: $(python.version) # Install build dependencies. - script: | @@ -24,15 +24,14 @@ steps: sudo apt-get install -y --allow-unauthenticated libopenblas-dev g++ xz-utils - if [ "$(binding)" == "python" ]; then - export PYBIN=$(which python) - $PYBIN -m pip install --upgrade pip - $PYBIN -m pip install --upgrade --ignore-installed setuptools cython pandas wheel + if [ "$BINDING" = "python" ]; then + python -m pip install --upgrade pip + python -m pip install --upgrade --ignore-installed setuptools cython pandas wheel fi - if [ "a$(julia.version)" != "a" ]; then - wget https://julialang-s3.julialang.org/bin/linux/x64/1.6/julia-1.6.3-linux-x86_64.tar.gz - sudo tar -C /opt/ -xvpf julia-1.6.3-linux-x86_64.tar.gz + if [ "$BINDING" = "julia" ]; then + wget https://julialang-s3.julialang.org/bin/linux/x64/1.10/julia-1.10.4-linux-x86_64.tar.gz + sudo tar -C /opt/ -xvpf julia-1.10.4-linux-x86_64.tar.gz fi # Install armadillo. @@ -70,7 +69,7 @@ steps: - script: | unset BOOST_ROOT mkdir build && cd build - if [ "$(binding)" == "go" ]; then + if [ "$BINDING" = "go" ]; then export GOPATH=$PWD/src/mlpack/bindings/go export GO111MODULE=off go get -u -t gonum.org/v1/gonum/... diff --git a/.ci/macos-steps.yaml b/.ci/macos-steps.yaml index 0607b6d431..2581bac608 100644 --- a/.ci/macos-steps.yaml +++ b/.ci/macos-steps.yaml @@ -15,12 +15,12 @@ steps: sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer brew install libomp openblas armadillo cereal ensmallen - if [ "$(binding)" == "python" ]; then + if [ "$BINDING" = "python" ]; then pip install --upgrade pip pip install cython numpy pandas zipp configparser wheel fi - if [ "a$(julia.version)" != "a" ]; then + if [ "$BINDING" = "julia" ]; then brew install --cask julia fi @@ -29,14 +29,13 @@ steps: # Configure mlpack (CMake) - script: | mkdir build && cd build - if [ "$(binding)" == "go" ]; then + if [ "$BINDING" = "go" ]; then export GOPATH=$PWD/src/mlpack/bindings/go export GO111MODULE=off go get -u -t gonum.org/v1/gonum/... fi - if [ "$(binding)" == "python" ]; then - export PYPATH=$(which python) - cmake $(CMakeArgs) -DPYTHON_EXECUTABLE=$PYPATH .. + if [ "$BINDING" = "python" ]; then + cmake $(CMakeArgs) -DPYTHON_EXECUTABLE=$(which python) .. else cmake $(CMakeArgs) .. fi From 3fddc2fffc96ef8367aad16b46743d9ded08f7c4 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Fri, 12 Jul 2024 16:25:09 +0200 Subject: [PATCH 117/212] opt: implemented blocks for a better cache utilization --- .../methods/kmeans/naive_kmeans_impl.hpp | 129 ++++++++++++------ 1 file changed, 86 insertions(+), 43 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index fa981e806b..8d789c2b80 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -35,43 +35,84 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, arma::mat& newCentroids, arma::Col& counts) { - newCentroids.zeros(centroids.n_rows, centroids.n_cols); - counts.zeros(centroids.n_cols); + const size_t dims = dataset.n_rows; + const size_t points = dataset.n_cols; + const size_t clusters = centroids.n_cols; - // Find the closest centroid to each point and update the new centroids. - // Computed in parallel over the complete dataset + newCentroids.zeros(dims, clusters); + counts.zeros(clusters); + + // Pre-compute squared norms of centroids + arma::vec centroidNorms(clusters); + #pragma omp parallel for schedule(static) + for (size_t j = 0; j < clusters; ++j) + { + centroidNorms(j) = arma::dot(centroids.col(j), centroids.col(j)); + } + + // Block-wise for a better cache utilization + const size_t blockSize = 256; + #pragma omp parallel { - // The current state of the K-means is private for each thread - arma::mat localCentroids(centroids.n_rows, centroids.n_cols, - arma::fill::zeros); - arma::Col localCounts(centroids.n_cols, arma::fill::zeros); + arma::mat localCentroids(dims, clusters, arma::fill::zeros); + arma::Col localCounts(clusters, arma::fill::zeros); - #pragma omp for schedule(dynamic) - for (size_t i = 0; i < (size_t) dataset.n_cols; ++i) + #pragma omp for schedule(dynamic, 1) + for (size_t block = 0; block < points; block += blockSize) { - // Find the closest centroid to this point. - double minDistance = std::numeric_limits::infinity(); - size_t closestCluster = centroids.n_cols; // Invalid value. - - for (size_t j = 0; j < centroids.n_cols; ++j) + const size_t blockEnd = std::min(block + blockSize, points); + + for (size_t i = block; i < blockEnd; ++i) { - const double dist = distance.Evaluate(dataset.col(i), - centroids.unsafe_col(j)); - if (dist < minDistance) + double minDistance = std::numeric_limits::max(); + size_t closestCluster = clusters; + + const double* dataPoint = dataset.colptr(i); + double dataNorm = 0.0; + + // Compute data point norm + #pragma omp simd reduction(+:dataNorm) + for (size_t d = 0; d < dims; ++d) { - minDistance = dist; - closestCluster = j; + dataNorm += dataPoint[d] * dataPoint[d]; } + + // Find closest centroid + for (size_t j = 0; j < clusters; ++j) + { + const double* centroid = centroids.colptr(j); + double dotProduct = 0.0; + + // Compute dot product + #pragma omp simd reduction(+:dotProduct) + for (size_t d = 0; d < dims; ++d) + { + dotProduct += dataPoint[d] * centroid[d]; + } + + // Use squared Euclidean distance + double dist = dataNorm + centroidNorms(j) - 2 * dotProduct; + + if (dist < minDistance) + { + minDistance = dist; + closestCluster = j; + } + } + + // Update local centroids and counts + double* localCentroidCol = localCentroids.colptr(closestCluster); + #pragma omp simd + for (size_t d = 0; d < dims; ++d) + { + localCentroidCol[d] += dataPoint[d]; + } + localCounts(closestCluster)++; } - - Log::Assert(closestCluster != centroids.n_cols); - - // We now have the minimum distance centroid index. Update that centroid. - localCentroids.unsafe_col(closestCluster) += dataset.col(i); - localCounts(closestCluster)++; } - // Combine calculated state from each thread using atomic operations + + // Combine results #pragma omp critical { newCentroids += localCentroids; @@ -79,25 +120,27 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, } } - // Now normalize the centroids. - #pragma omp parallel for - for (size_t i = 0; i < centroids.n_cols; ++i) - if (counts(i) != 0) - newCentroids.col(i) /= counts(i); - - distanceCalculations += centroids.n_cols * dataset.n_cols; - - // Calculate cluster distortion for this iteration. - double cNorm = 0.0; - #pragma omp parallel for reduction(+:cNorm) - for (size_t i = 0; i < centroids.n_cols; ++i) + // Normalize the centroids + #pragma omp parallel for schedule(static) + for (size_t j = 0; j < clusters; ++j) { - cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), - 2.0); + if (counts(j) > 0) + { + newCentroids.col(j) /= counts(j); + } } - distanceCalculations += centroids.n_cols; - return std::sqrt(cNorm); + // Calculate cluster distortion + double cNorm = 0.0; + #pragma omp parallel for reduction(+:cNorm) schedule(static) + for (size_t j = 0; j < clusters; ++j) + { + cNorm += arma::norm(centroids.col(j) - newCentroids.col(j), 2); + } + + distanceCalculations += clusters * points; + + return cNorm; } } // namespace mlpack From cd08dff039ecd02695dd7022f91972bdf64fd044 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 12 Jul 2024 10:26:33 -0400 Subject: [PATCH 118/212] Oops, this is Azure Pipelines not Github actions... --- .ci/ci.yaml | 18 ++++++------------ .ci/linux-steps.yaml | 8 ++++---- .ci/macos-steps.yaml | 12 ++++++------ 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/.ci/ci.yaml b/.ci/ci.yaml index 1805f91c59..1e36d4a07c 100644 --- a/.ci/ci.yaml +++ b/.ci/ci.yaml @@ -20,17 +20,14 @@ jobs: CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF' Python: CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=ON -DPYTHON_EXECUTABLE=/usr/bin/python3 -DBUILD_GO_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF' + binding: 'python' python.version: '3.7' - env: - BINDING: 'python' Julia: CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=ON -DBUILD_GO_BINDINGS=OFF -DJULIA_EXECUTABLE=/opt/julia-1.6.3/bin/julia -DBUILD_R_BINDINGS=OFF' - env: - BINDING: 'julia' + binding: 'julia' Go: CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=ON -DBUILD_R_BINDINGS=OFF' - env: - BINDING: 'go' + binding: 'go' Markdown: CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_MARKDOWN_BINDINGS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF' @@ -50,19 +47,16 @@ jobs: CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF' Python: python.version: '3.8' + binding: 'python' CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=ON -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF' - env: - BINDING: 'python' Julia: python.version: '3.8' + binding: 'julia' CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_JULIA_BINDINGS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF' - env: - BINDING: 'julia' Go: python.version: '3.8' + binding: 'go' CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=ON -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF' - env: - BINDING: 'go' steps: - template: macos-steps.yaml diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml index 9b288d76ed..7c0786b7ff 100644 --- a/.ci/linux-steps.yaml +++ b/.ci/linux-steps.yaml @@ -24,12 +24,12 @@ steps: sudo apt-get install -y --allow-unauthenticated libopenblas-dev g++ xz-utils - if [ "$BINDING" = "python" ]; then + if [ "$binding" = "python" ]; then python -m pip install --upgrade pip python -m pip install --upgrade --ignore-installed setuptools cython pandas wheel fi - if [ "$BINDING" = "julia" ]; then + if [ "$binding" = "julia" ]; then wget https://julialang-s3.julialang.org/bin/linux/x64/1.10/julia-1.10.4-linux-x86_64.tar.gz sudo tar -C /opt/ -xvpf julia-1.10.4-linux-x86_64.tar.gz fi @@ -69,12 +69,12 @@ steps: - script: | unset BOOST_ROOT mkdir build && cd build - if [ "$BINDING" = "go" ]; then + if [ "$binding" = "go" ]; then export GOPATH=$PWD/src/mlpack/bindings/go export GO111MODULE=off go get -u -t gonum.org/v1/gonum/... fi - cmake $(CMakeArgs) -DPYTHON_EXECUTABLE=`which python` -DCEREAL_INCLUDE_DIR=/usr/include/ .. + cmake $CMAKE_ARGS -DPYTHON_EXECUTABLE=`which python` -DCEREAL_INCLUDE_DIR=/usr/include/ .. displayName: 'CMake' # Build mlpack diff --git a/.ci/macos-steps.yaml b/.ci/macos-steps.yaml index 2581bac608..7bc2e64d44 100644 --- a/.ci/macos-steps.yaml +++ b/.ci/macos-steps.yaml @@ -15,12 +15,12 @@ steps: sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer brew install libomp openblas armadillo cereal ensmallen - if [ "$BINDING" = "python" ]; then + if [ "$binding" = "python" ]; then pip install --upgrade pip pip install cython numpy pandas zipp configparser wheel fi - if [ "$BINDING" = "julia" ]; then + if [ "$binding" = "julia" ]; then brew install --cask julia fi @@ -29,15 +29,15 @@ steps: # Configure mlpack (CMake) - script: | mkdir build && cd build - if [ "$BINDING" = "go" ]; then + if [ "$binding" = "go" ]; then export GOPATH=$PWD/src/mlpack/bindings/go export GO111MODULE=off go get -u -t gonum.org/v1/gonum/... fi - if [ "$BINDING" = "python" ]; then - cmake $(CMakeArgs) -DPYTHON_EXECUTABLE=$(which python) .. + if [ "$binding" = "python" ]; then + cmake $CMakeArgs -DPYTHON_EXECUTABLE=$(which python) .. else - cmake $(CMakeArgs) .. + cmake $CMakeArgs .. fi displayName: 'CMake' From 43218e8270d9490c5dc36414c4fb521117bdd059 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 12 Jul 2024 20:52:07 -0400 Subject: [PATCH 119/212] Attempt to fix python.version usage. --- .ci/linux-steps.yaml | 3 +-- .ci/macos-steps.yaml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml index 7c0786b7ff..b8c08e7bfc 100644 --- a/.ci/linux-steps.yaml +++ b/.ci/linux-steps.yaml @@ -7,7 +7,7 @@ steps: # Set python version - task: UsePythonVersion@0 inputs: - versionSpec: $(python.version) + versionSpec: ${{ python.version }} # Install build dependencies. - script: | @@ -67,7 +67,6 @@ steps: # Configure mlpack (CMake) - script: | - unset BOOST_ROOT mkdir build && cd build if [ "$binding" = "go" ]; then export GOPATH=$PWD/src/mlpack/bindings/go diff --git a/.ci/macos-steps.yaml b/.ci/macos-steps.yaml index 7bc2e64d44..c8b2c4a107 100644 --- a/.ci/macos-steps.yaml +++ b/.ci/macos-steps.yaml @@ -7,7 +7,7 @@ steps: # Set python version. - task: UsePythonVersion@0 inputs: - versionSpec: '$(python.version)' + versionSpec: ${{ python.version }} # Install Build Dependencies - script: | From d016cc7fe7932a1e7100df119e55f325f6708d78 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 12 Jul 2024 20:58:18 -0400 Subject: [PATCH 120/212] Maybe if I'm lucky this will work. --- .ci/linux-steps.yaml | 2 +- .ci/macos-steps.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml index b8c08e7bfc..9b34bc16f3 100644 --- a/.ci/linux-steps.yaml +++ b/.ci/linux-steps.yaml @@ -7,7 +7,7 @@ steps: # Set python version - task: UsePythonVersion@0 inputs: - versionSpec: ${{ python.version }} + versionSpec: ${{ variables.python.version }} # Install build dependencies. - script: | diff --git a/.ci/macos-steps.yaml b/.ci/macos-steps.yaml index c8b2c4a107..970464573d 100644 --- a/.ci/macos-steps.yaml +++ b/.ci/macos-steps.yaml @@ -7,7 +7,7 @@ steps: # Set python version. - task: UsePythonVersion@0 inputs: - versionSpec: ${{ python.version }} + versionSpec: ${{ variables.python.version }} # Install Build Dependencies - script: | From 07482562d2a25eb5a53b0c5c7294bd44fad5263d Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 12 Jul 2024 21:18:19 -0400 Subject: [PATCH 121/212] Okay, well is this the format that works? --- .ci/linux-steps.yaml | 2 +- .ci/macos-steps.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml index 9b34bc16f3..c6c86b9adb 100644 --- a/.ci/linux-steps.yaml +++ b/.ci/linux-steps.yaml @@ -7,7 +7,7 @@ steps: # Set python version - task: UsePythonVersion@0 inputs: - versionSpec: ${{ variables.python.version }} + versionSpec: $(variables.python.version) # Install build dependencies. - script: | diff --git a/.ci/macos-steps.yaml b/.ci/macos-steps.yaml index 970464573d..bfc7fd5d01 100644 --- a/.ci/macos-steps.yaml +++ b/.ci/macos-steps.yaml @@ -7,7 +7,7 @@ steps: # Set python version. - task: UsePythonVersion@0 inputs: - versionSpec: ${{ variables.python.version }} + versionSpec: $(variables.python.version) # Install Build Dependencies - script: | From f936797b8f68a78e147634cb3cf5c0b4c875b952 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 12 Jul 2024 21:23:08 -0400 Subject: [PATCH 122/212] The documentation says this is the way it should be. --- .ci/linux-steps.yaml | 2 +- .ci/macos-steps.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml index c6c86b9adb..8d592919ba 100644 --- a/.ci/linux-steps.yaml +++ b/.ci/linux-steps.yaml @@ -7,7 +7,7 @@ steps: # Set python version - task: UsePythonVersion@0 inputs: - versionSpec: $(variables.python.version) + versionSpec: '$(python.version)' # Install build dependencies. - script: | diff --git a/.ci/macos-steps.yaml b/.ci/macos-steps.yaml index bfc7fd5d01..7bc2e64d44 100644 --- a/.ci/macos-steps.yaml +++ b/.ci/macos-steps.yaml @@ -7,7 +7,7 @@ steps: # Set python version. - task: UsePythonVersion@0 inputs: - versionSpec: $(variables.python.version) + versionSpec: '$(python.version)' # Install Build Dependencies - script: | From 3aeed6059e14b1e80e27bd818d715f21d19f7d4c Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 12 Jul 2024 21:35:21 -0400 Subject: [PATCH 123/212] Okay, don't install Python if the variable isn't set (hopefully). --- .ci/linux-steps.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml index 8d592919ba..c66818aef2 100644 --- a/.ci/linux-steps.yaml +++ b/.ci/linux-steps.yaml @@ -8,6 +8,7 @@ steps: - task: UsePythonVersion@0 inputs: versionSpec: '$(python.version)' + condition: ne(variables['python.version'], '') # Install build dependencies. - script: | From 3808650a6b034ab2ab86e9f7df5f49bf9432930f Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 12 Jul 2024 22:00:34 -0400 Subject: [PATCH 124/212] Fix name of CMake options variable. --- .ci/ci.yaml | 2 +- .ci/linux-steps.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/ci.yaml b/.ci/ci.yaml index 1e36d4a07c..ab57443861 100644 --- a/.ci/ci.yaml +++ b/.ci/ci.yaml @@ -23,7 +23,7 @@ jobs: binding: 'python' python.version: '3.7' Julia: - CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=ON -DBUILD_GO_BINDINGS=OFF -DJULIA_EXECUTABLE=/opt/julia-1.6.3/bin/julia -DBUILD_R_BINDINGS=OFF' + CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=ON -DBUILD_GO_BINDINGS=OFF -DJULIA_EXECUTABLE=/opt/julia-1.10.4/bin/julia -DBUILD_R_BINDINGS=OFF' binding: 'julia' Go: CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=ON -DBUILD_R_BINDINGS=OFF' diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml index c66818aef2..048470d4ff 100644 --- a/.ci/linux-steps.yaml +++ b/.ci/linux-steps.yaml @@ -74,7 +74,7 @@ steps: export GO111MODULE=off go get -u -t gonum.org/v1/gonum/... fi - cmake $CMAKE_ARGS -DPYTHON_EXECUTABLE=`which python` -DCEREAL_INCLUDE_DIR=/usr/include/ .. + cmake $CMakeArgs -DPYTHON_EXECUTABLE=`which python` -DCEREAL_INCLUDE_DIR=/usr/include/ .. displayName: 'CMake' # Build mlpack From a1ecfabbe33f4bfec65c50a842a98b74b15aa3fb Mon Sep 17 00:00:00 2001 From: conradsnicta Date: Sat, 13 Jul 2024 09:04:48 +0200 Subject: [PATCH 125/212] Bump minimum Armadillo version to 10.8 (#3760) --- .ci/linux-steps.yaml | 2 +- .ci/windows-steps.yaml | 12 +++--- CMakeLists.txt | 2 +- HISTORY.md | 2 + README.md | 4 +- .../bindings/go/mlpack/capi/arma_util.hpp | 4 +- src/mlpack/bindings/julia/julia_util.cpp | 28 ++++--------- .../bindings/python/mlpack/arma_util.hpp | 5 +-- src/mlpack/core/util/arma_traits.hpp | 40 +++++-------------- 9 files changed, 33 insertions(+), 66 deletions(-) diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml index ebaa342552..b97f05285c 100644 --- a/.ci/linux-steps.yaml +++ b/.ci/linux-steps.yaml @@ -36,7 +36,7 @@ steps: fi # Install armadillo. - curl -k -L https://sourceforge.net/projects/arma/files/armadillo-9.800.6.tar.xz | tar -xvJ && \ + curl -k -L https://sourceforge.net/projects/arma/files/armadillo-10.8.2.tar.xz | tar -xvJ && \ cd armadillo* && \ cmake . && \ make && \ diff --git a/.ci/windows-steps.yaml b/.ci/windows-steps.yaml index 2d3c13058f..c40acff1a5 100644 --- a/.ci/windows-steps.yaml +++ b/.ci/windows-steps.yaml @@ -27,10 +27,10 @@ steps: - bash: | git clone --depth 1 https://github.com/mlpack/jenkins-conf.git conf - curl -O -L https://sourceforge.net/projects/arma/files/armadillo-9.800.6.tar.xz -o armadillo-9.800.6.tar.xz - tar -xvf armadillo-9.800.6.tar.xz + curl -O -L https://sourceforge.net/projects/arma/files/armadillo-10.8.2.tar.xz -o armadillo-10.8.2.tar.xz + tar -xvf armadillo-10.8.2.tar.xz - cd armadillo-9.800.6/ && cmake $(CMakeGenerator) \ + cd armadillo-10.8.2/ && cmake $(CMakeGenerator) \ -DBLAS_LIBRARY:FILEPATH=$(Agent.ToolsDirectory)/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a \ -DLAPACK_LIBRARY:FILEPATH=$(Agent.ToolsDirectory)/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a \ -DCMAKE_PREFIX:FILEPATH=../../armadillo \ @@ -41,7 +41,7 @@ steps: # Build armadillo - task: MSBuild@1 inputs: - solution: 'armadillo-9.800.6/*.sln' + solution: 'armadillo-10.8.2/*.sln' msbuildLocationMethod: 'location' msbuildVersion: $(MSBuildVersion) configuration: 'Release' @@ -60,8 +60,8 @@ steps: $(CMakeArgs) ` -DBLAS_LIBRARIES:FILEPATH=$(Agent.ToolsDirectory)\OpenBLAS.0.2.14.1\lib\native\lib\x64\libopenblas.dll.a ` -DLAPACK_LIBRARIES:FILEPATH=$(Agent.ToolsDirectory)\OpenBLAS.0.2.14.1\lib\native\lib\x64\libopenblas.dll.a ` - -DARMADILLO_INCLUDE_DIR="..\armadillo-9.800.6\tmp\include" ` - -DARMADILLO_LIBRARY="..\armadillo-9.800.6\Release\armadillo.lib" ` + -DARMADILLO_INCLUDE_DIR="..\armadillo-10.8.2\tmp\include" ` + -DARMADILLO_LIBRARY="..\armadillo-10.8.2\Release\armadillo.lib" ` -DCEREAL_INCLUDE_DIR="..\cereal-1.3.2\include" ` -DENSMALLEN_INCLUDE_DIR=$(Agent.ToolsDirectory)\ensmallen.2.17.0\installed\x64-linux\include ` -DBUILD_JULIA_BINDINGS=OFF ` diff --git a/CMakeLists.txt b/CMakeLists.txt index 75f6fe1b9d..f530697ca9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ option(USE_PRECOMPILED_HEADERS "Use precompiled headers for mlpack_test build." # For Armadillo, try to keep the minimum required version less than or equal to # what's available on the current Ubuntu LTS or most recent stable RHEL release. # See https://github.com/mlpack/mlpack/issues/3033 for some more discussion. -set(ARMADILLO_VERSION "9.800") +set(ARMADILLO_VERSION "10.8") set(ENSMALLEN_VERSION "2.10.0") set(CEREAL_VERSION "1.1.2") diff --git a/HISTORY.md b/HISTORY.md index e98875f83b..3efc2e024d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,6 +11,8 @@ _????-??-??_ * Implemented the Find and Fill algorithm into the Dropout Layer and added OpenMP support (#3684). * Update Python bindings to support NumPy 2.x (#3752). + + * Bump minimum Armadillo version to 10.8 (#3760). ## mlpack 4.4.0 diff --git a/README.md b/README.md index 7bf1bf301a..70c3c2ab01 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Citations are beneficial for the growth and improvement of mlpack. **mlpack** requires the following additional dependencies: - C++17 compiler - - [Armadillo](https://arma.sourceforge.net)   >= 9.800 + - [Armadillo](https://arma.sourceforge.net)   >= 10.8 - [ensmallen](https://ensmallen.org)  >= 2.10.0 - [cereal](http://uscilab.github.io/cereal/)     >= 1.1.2 @@ -333,7 +333,7 @@ dependencies are installed: - R >= 4.0 - Rcpp >= 0.12.12 - - RcppArmadillo >= 0.9.800.0 + - RcppArmadillo >= 0.10.8.0 - RcppEnsmallen >= 0.2.10.0 - roxygen2 - testthat diff --git a/src/mlpack/bindings/go/mlpack/capi/arma_util.hpp b/src/mlpack/bindings/go/mlpack/capi/arma_util.hpp index 0c57590e26..4034934ba7 100644 --- a/src/mlpack/bindings/go/mlpack/capi/arma_util.hpp +++ b/src/mlpack/bindings/go/mlpack/capi/arma_util.hpp @@ -39,9 +39,7 @@ inline typename T::elem_type* GetMemory(T& m) arma::access::rw(m.mem_state) = 1; // With Armadillo 10 and newer, we must set `n_alloc` to 0 so that // Armadillo does not deallocate the memory. - #if ARMA_VERSION_MAJOR >= 10 - arma::access::rw(m.n_alloc) = 0; - #endif + arma::access::rw(m.n_alloc) = 0; return m.memptr(); } } diff --git a/src/mlpack/bindings/julia/julia_util.cpp b/src/mlpack/bindings/julia/julia_util.cpp index 5c0320a311..c3340296a6 100644 --- a/src/mlpack/bindings/julia/julia_util.cpp +++ b/src/mlpack/bindings/julia/julia_util.cpp @@ -428,9 +428,7 @@ double* GetParamMat(void* params, const char* paramName) else { arma::access::rw(mat.mem_state) = 1; - #if ARMA_VERSION_MAJOR >= 10 - arma::access::rw(mat.n_alloc) = 0; - #endif + arma::access::rw(mat.n_alloc) = 0; return mat.memptr(); } } @@ -475,9 +473,7 @@ size_t* GetParamUMat(void* params, const char* paramName) else { arma::access::rw(mat.mem_state) = 1; - #if ARMA_VERSION_MAJOR >= 10 - arma::access::rw(mat.n_alloc) = 0; - #endif + arma::access::rw(mat.n_alloc) = 0; return mat.memptr(); } } @@ -513,9 +509,7 @@ double* GetParamCol(void* params, const char* paramName) else { arma::access::rw(vec.mem_state) = 1; - #if ARMA_VERSION_MAJOR >= 10 - arma::access::rw(vec.n_alloc) = 0; - #endif + arma::access::rw(vec.n_alloc) = 0; return vec.memptr(); } } @@ -552,9 +546,7 @@ size_t* GetParamUCol(void* params, const char* paramName) else { arma::access::rw(vec.mem_state) = 1; - #if ARMA_VERSION_MAJOR >= 10 - arma::access::rw(vec.n_alloc) = 0; - #endif + arma::access::rw(vec.n_alloc) = 0; return vec.memptr(); } } @@ -590,9 +582,7 @@ double* GetParamRow(void* params, const char* paramName) else { arma::access::rw(vec.mem_state) = 1; - #if ARMA_VERSION_MAJOR >= 10 - arma::access::rw(vec.n_alloc) = 0; - #endif + arma::access::rw(vec.n_alloc) = 0; return vec.memptr(); } } @@ -629,9 +619,7 @@ size_t* GetParamURow(void* params, const char* paramName) else { arma::access::rw(vec.mem_state) = 1; - #if ARMA_VERSION_MAJOR >= 10 - arma::access::rw(vec.n_alloc) = 0; - #endif + arma::access::rw(vec.n_alloc) = 0; return vec.memptr(); } } @@ -707,9 +695,7 @@ double* GetParamMatWithInfoPtr(void* params, const char* paramName) else { arma::access::rw(m.mem_state) = 1; - #if ARMA_VERSION_MAJOR >= 10 - arma::access::rw(m.n_alloc) = 0; - #endif + arma::access::rw(m.n_alloc) = 0; return m.memptr(); } } diff --git a/src/mlpack/bindings/python/mlpack/arma_util.hpp b/src/mlpack/bindings/python/mlpack/arma_util.hpp index 70f0dd1b3e..ca6a8d1a71 100644 --- a/src/mlpack/bindings/python/mlpack/arma_util.hpp +++ b/src/mlpack/bindings/python/mlpack/arma_util.hpp @@ -25,9 +25,8 @@ void SetMemState(T& t, int state) // If we just "released" the memory, so that the matrix does not own it, with // Armadillo 10 we must also ensure that the matrix does not deallocate the // memory by specifying `n_alloc = 0`. - #if ARMA_VERSION_MAJOR >= 10 - const_cast(t.n_alloc) = 0; - #endif + + const_cast(t.n_alloc) = 0; } /** diff --git a/src/mlpack/core/util/arma_traits.hpp b/src/mlpack/core/util/arma_traits.hpp index 2dd360761b..1a7133feb5 100644 --- a/src/mlpack/core/util/arma_traits.hpp +++ b/src/mlpack/core/util/arma_traits.hpp @@ -50,7 +50,7 @@ struct IsCube }; // Commenting out the first template per case, because -// Visual Studio doesn't like this instantiaion pattern (error C2910). +// Visual Studio doesn't like this instantiation pattern (error C2910). // template<> template struct IsVector > @@ -105,35 +105,17 @@ struct IsCube > const static bool value = true; }; +template +struct IsVector > +{ + const static bool value = true; +}; -#if ((ARMA_VERSION_MAJOR >= 10) || \ - ((ARMA_VERSION_MAJOR == 9) && (ARMA_VERSION_MINOR >= 869))) - - // Armadillo 9.869+ has SpSubview_col and SpSubview_row - - template - struct IsVector > - { - const static bool value = true; - }; - - template - struct IsVector > - { - const static bool value = true; - }; - -#else - - // fallback for older Armadillo versions - - template - struct IsVector > - { - const static bool value = true; - }; - -#endif +template +struct IsVector > +{ + const static bool value = true; +}; // Get the row vector type corresponding to a given MatType. From 7166940b4cbbf833e48390b2fac63a8eca1cfcdc Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 13 Jul 2024 13:31:22 -0400 Subject: [PATCH 126/212] Try to figure out why CMakeArgs is empty. --- .ci/linux-steps.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml index 048470d4ff..4952764c05 100644 --- a/.ci/linux-steps.yaml +++ b/.ci/linux-steps.yaml @@ -21,6 +21,10 @@ steps: free -h df -h + echo "binding: $binding" + echo "CMakeArgs: $CMakeArgs" + env + git clone --depth 1 https://github.com/mlpack/jenkins-conf.git conf sudo apt-get install -y --allow-unauthenticated libopenblas-dev g++ xz-utils @@ -74,6 +78,9 @@ steps: export GO111MODULE=off go get -u -t gonum.org/v1/gonum/... fi + echo "binding: $binding" + echo "CMakeArgs: $CMakeArgs" + env cmake $CMakeArgs -DPYTHON_EXECUTABLE=`which python` -DCEREAL_INCLUDE_DIR=/usr/include/ .. displayName: 'CMake' From 71429d3f78ab6975f08b774de6b49db5b37153d2 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 13 Jul 2024 13:51:00 -0400 Subject: [PATCH 127/212] Ok so they get set as all-caps variables? --- .ci/linux-steps.yaml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml index 4952764c05..655bf65735 100644 --- a/.ci/linux-steps.yaml +++ b/.ci/linux-steps.yaml @@ -21,20 +21,16 @@ steps: free -h df -h - echo "binding: $binding" - echo "CMakeArgs: $CMakeArgs" - env - git clone --depth 1 https://github.com/mlpack/jenkins-conf.git conf sudo apt-get install -y --allow-unauthenticated libopenblas-dev g++ xz-utils - if [ "$binding" = "python" ]; then + if [ "$BINDING" = "python" ]; then python -m pip install --upgrade pip python -m pip install --upgrade --ignore-installed setuptools cython pandas wheel fi - if [ "$binding" = "julia" ]; then + if [ "$BINDING" = "julia" ]; then wget https://julialang-s3.julialang.org/bin/linux/x64/1.10/julia-1.10.4-linux-x86_64.tar.gz sudo tar -C /opt/ -xvpf julia-1.10.4-linux-x86_64.tar.gz fi @@ -73,15 +69,12 @@ steps: # Configure mlpack (CMake) - script: | mkdir build && cd build - if [ "$binding" = "go" ]; then + if [ "$BINDING" = "go" ]; then export GOPATH=$PWD/src/mlpack/bindings/go export GO111MODULE=off go get -u -t gonum.org/v1/gonum/... fi - echo "binding: $binding" - echo "CMakeArgs: $CMakeArgs" - env - cmake $CMakeArgs -DPYTHON_EXECUTABLE=`which python` -DCEREAL_INCLUDE_DIR=/usr/include/ .. + cmake $CMAKEARGS -DPYTHON_EXECUTABLE=`which python` -DCEREAL_INCLUDE_DIR=/usr/include/ .. displayName: 'CMake' # Build mlpack From 87a105f3fd9536c058eb64728bc7b0d2a64d5cf7 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 13 Jul 2024 15:32:55 -0400 Subject: [PATCH 128/212] Fix environment variables for macOS builds. --- .ci/macos-steps.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.ci/macos-steps.yaml b/.ci/macos-steps.yaml index 7bc2e64d44..d6f177f28a 100644 --- a/.ci/macos-steps.yaml +++ b/.ci/macos-steps.yaml @@ -15,12 +15,12 @@ steps: sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer brew install libomp openblas armadillo cereal ensmallen - if [ "$binding" = "python" ]; then + if [ "$BINDING" = "python" ]; then pip install --upgrade pip pip install cython numpy pandas zipp configparser wheel fi - if [ "$binding" = "julia" ]; then + if [ "$BINDING" = "julia" ]; then brew install --cask julia fi @@ -29,15 +29,15 @@ steps: # Configure mlpack (CMake) - script: | mkdir build && cd build - if [ "$binding" = "go" ]; then + if [ "$BINDING" = "go" ]; then export GOPATH=$PWD/src/mlpack/bindings/go export GO111MODULE=off go get -u -t gonum.org/v1/gonum/... fi - if [ "$binding" = "python" ]; then - cmake $CMakeArgs -DPYTHON_EXECUTABLE=$(which python) .. + if [ "$BINDING" = "python" ]; then + cmake $CMAKEARGS -DPYTHON_EXECUTABLE=$(which python) .. else - cmake $CMakeArgs .. + cmake $CMAKEARGS .. fi displayName: 'CMake' From e936cc9b7010f1243952fae2a73229a483db2b1d Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 15 Jul 2024 09:20:02 -0400 Subject: [PATCH 129/212] Don't fully specify version number. --- .github/workflows/auto-approve.yml | 2 +- .github/workflows/stickers.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml index 0e4c4b81a3..9a8ab15f92 100644 --- a/.github/workflows/auto-approve.yml +++ b/.github/workflows/auto-approve.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Auto-approve pull requests - uses: rcurtin/auto-approve@v1.0.2 + uses: rcurtin/auto-approve@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} approval-message: diff --git a/.github/workflows/stickers.yaml b/.github/workflows/stickers.yaml index bfaa763f25..b6aa2f5667 100644 --- a/.github/workflows/stickers.yaml +++ b/.github/workflows/stickers.yaml @@ -11,7 +11,7 @@ jobs: if: github.event.pull_request.merged == true steps: # Forked version of first-interaction that runs only on first merged PR. - - uses: rcurtin/first-interaction@v1.0.1 + - uses: rcurtin/first-interaction@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} pr-message: "Hello there! Thanks for your contribution. Congratulations on your first contribution to mlpack! If you'd like to add your name to the list of contributors in `COPYRIGHT.txt` and you haven't already, please feel free to push a change to this PR---or, if it gets merged before you can, feel free to open another PR.\n\nIn addition, if you'd like some stickers to put on your laptop, we can get them in the mail for you. Just send an email with your physical mailing address to stickers@mlpack.org, and then one of the mlpack maintainers will put some stickers in an envelope for you. It may take a few weeks to get them, depending on your location. :+1:" From 389bb88db3e6fa395be38af1514a7e4fffa758a2 Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Mon, 15 Jul 2024 19:10:54 +0530 Subject: [PATCH 130/212] revert sse_gain --- .../loss_functions/sse_loss.hpp} | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) rename src/mlpack/methods/{decision_tree/fitness_functions/sse_gain.hpp => xgboost/loss_functions/sse_loss.hpp} (81%) diff --git a/src/mlpack/methods/decision_tree/fitness_functions/sse_gain.hpp b/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp similarity index 81% rename from src/mlpack/methods/decision_tree/fitness_functions/sse_gain.hpp rename to src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp index b369128aaa..1029fa06af 100644 --- a/src/mlpack/methods/decision_tree/fitness_functions/sse_gain.hpp +++ b/src/mlpack/methods/xgboost/loss_functions/sse_loss.hpp @@ -1,8 +1,8 @@ /** - * @file methods/decision_tree/gain_functions/sse_gain.hpp + * @file methods/xgboost/loss_functions/sse_loss.hpp * @author Rishabh Garg * - * The sum of squared error loss class, which is a loss function for gradient + * The sum of squared error loss class, which is a loss funtion for gradient * xgboost based decision trees. * * mlpack is free software; you may redistribute it and/or modify it under the @@ -10,8 +10,8 @@ * 3-clause BSD license along with mlpack. If not, see * http://www.opensource.org/licenses/BSD-3-Clause for more information. */ -#ifndef MLPACK_METHODS_DECISION_TREE_SSE_GAIN_HPP -#define MLPACK_METHODS_DECISION_TREE_SSE_GAIN_HPP +#ifndef MLPACK_METHODS_XGBOOST_LOSS_FUNCTIONS_SSE_LOSS_HPP +#define MLPACK_METHODS_XGBOOST_LOSS_FUNCTIONS_SSE_LOSS_HPP #include @@ -25,13 +25,13 @@ namespace mlpack { * * Loss = 1 / 2 * (Observed - Predicted)^2 */ -class SSEGain +class SSELoss { public: // Default constructor---No regularization. - SSEGain() : alpha(0), lambda(0) { /* Nothing to do. */} + SSELoss() : alpha(0), lambda(0) { /* Nothing to do. */} - SSEGain(const double alpha, const double lambda): + SSELoss(const double alpha, const double lambda): alpha(alpha), lambda(lambda) { // Nothing to do. @@ -66,15 +66,8 @@ class SSEGain * @param begin The begin index to calculate gain. * @param end The end index to calculate gain. */ - template - double Evaluate(const MatType& input, - const WeightVecType& /* weights */, - const size_t begin, - const size_t end) + double Evaluate(const size_t begin, const size_t end) { - gradients = (input.row(1) - input.row(0)).t(); - hessians = arma::vec(input.n_cols, arma::fill::ones); - return std::pow(ApplyL1(accu(gradients.subvec(begin, end))), 2) / (accu(hessians.subvec(begin, end)) + lambda); } From d19d9be10b39b7b795705f021483a217c52e9edc Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Mon, 15 Jul 2024 19:12:02 +0530 Subject: [PATCH 131/212] revert tests --- src/mlpack/tests/xgboost_test.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mlpack/tests/xgboost_test.cpp b/src/mlpack/tests/xgboost_test.cpp index 57dbbc53dc..3c02c28b45 100644 --- a/src/mlpack/tests/xgboost_test.cpp +++ b/src/mlpack/tests/xgboost_test.cpp @@ -10,7 +10,7 @@ * http://www.opensource.org/licenses/BSD-3-Clause for more information. */ #include -#include +#include #include "catch.hpp" #include "serialization.hpp" @@ -18,7 +18,7 @@ using namespace mlpack; /** - * Test that the initial prediction is calculated correctly for SSE gain. + * Test that the initial prediction is calculated correctly for SSE loss. */ TEST_CASE("SSEInitialPredictionTest", "[XGBTest]") { @@ -26,12 +26,12 @@ TEST_CASE("SSEInitialPredictionTest", "[XGBTest]") double initPred = 5.5; - SSEGain Loss; + SSELoss Loss; REQUIRE(Loss.InitialPrediction(values) == initPred); } /** - * Test that output leaf value is calculated correctly for SSE gain. + * Test that output leaf value is calculated correctly for SSE Loss. */ TEST_CASE("SSELeafValueTest", "[XGBTest]") { @@ -42,14 +42,14 @@ TEST_CASE("SSELeafValueTest", "[XGBTest]") // Actual output leaf value. double leafValue = -0.075; - SSEGain Loss; + SSELoss Loss; (void) Loss.Evaluate(input, weights); REQUIRE(Loss.OutputLeafValue(input, weights) == leafValue); } /** - * Test that the gain is computed correctly for SSE gain. + * Test that the gain is computed correctly for SSE Loss. */ TEST_CASE("SSEGainTest", "[XGBTest]") { @@ -60,6 +60,6 @@ TEST_CASE("SSEGainTest", "[XGBTest]") // Actual gain value. double gain = 0.05625; - SSEGain Loss; + SSELoss Loss; REQUIRE(Loss.Evaluate(input, weights) == gain); -} +} \ No newline at end of file From abbba79f804733a97883fdeccb104b50d907ff4d Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Mon, 15 Jul 2024 19:16:19 +0530 Subject: [PATCH 132/212] minor fix --- .../methods/decision_tree/fitness_functions/gini_gain.hpp | 2 +- .../decision_tree/fitness_functions/information_gain.hpp | 2 +- src/mlpack/methods/decision_tree/fitness_functions/mad_gain.hpp | 2 +- src/mlpack/methods/decision_tree/fitness_functions/mse_gain.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/decision_tree/fitness_functions/gini_gain.hpp b/src/mlpack/methods/decision_tree/fitness_functions/gini_gain.hpp index e52a23d3af..34f6c4a7fb 100644 --- a/src/mlpack/methods/decision_tree/fitness_functions/gini_gain.hpp +++ b/src/mlpack/methods/decision_tree/fitness_functions/gini_gain.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/gain_functions/gini_gain.hpp + * @file methods/decision_tree/fitness_functions/gini_gain.hpp * @author Ryan Curtin * * The GiniGain class, which is a fitness function (FitnessFunction) for diff --git a/src/mlpack/methods/decision_tree/fitness_functions/information_gain.hpp b/src/mlpack/methods/decision_tree/fitness_functions/information_gain.hpp index a768886114..9c6719a23a 100644 --- a/src/mlpack/methods/decision_tree/fitness_functions/information_gain.hpp +++ b/src/mlpack/methods/decision_tree/fitness_functions/information_gain.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/gain_functions/information_gain.hpp + * @file methods/decision_tree/fitness_functions/information_gain.hpp * @author Ryan Curtin * * An implementation of information gain, which can be used in place of Gini diff --git a/src/mlpack/methods/decision_tree/fitness_functions/mad_gain.hpp b/src/mlpack/methods/decision_tree/fitness_functions/mad_gain.hpp index bcf1275e57..908af9b966 100644 --- a/src/mlpack/methods/decision_tree/fitness_functions/mad_gain.hpp +++ b/src/mlpack/methods/decision_tree/fitness_functions/mad_gain.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/gain_functions/mad_gain.hpp + * @file methods/decision_tree/fitness_functions/mad_gain.hpp * @author Rishabh Garg * * The mean absolute deviation gain class, a fitness function for regression diff --git a/src/mlpack/methods/decision_tree/fitness_functions/mse_gain.hpp b/src/mlpack/methods/decision_tree/fitness_functions/mse_gain.hpp index 8709b3e9ae..77687d2cd1 100644 --- a/src/mlpack/methods/decision_tree/fitness_functions/mse_gain.hpp +++ b/src/mlpack/methods/decision_tree/fitness_functions/mse_gain.hpp @@ -1,5 +1,5 @@ /** - * @file methods/decision_tree/gain_functions/mse_gain.hpp + * @file methods/decision_tree/fitness_functions/mse_gain.hpp * @author Rishabh Garg * * The mean squared error gain class, which is a fitness funtion for From facd8882ebc29c12af1935d3b686ba04baa8210c Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Mon, 15 Jul 2024 19:28:00 +0530 Subject: [PATCH 133/212] minor fix --- src/mlpack/tests/xgboost_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/tests/xgboost_test.cpp b/src/mlpack/tests/xgboost_test.cpp index 3c02c28b45..4b3d1d533a 100644 --- a/src/mlpack/tests/xgboost_test.cpp +++ b/src/mlpack/tests/xgboost_test.cpp @@ -62,4 +62,4 @@ TEST_CASE("SSEGainTest", "[XGBTest]") SSELoss Loss; REQUIRE(Loss.Evaluate(input, weights) == gain); -} \ No newline at end of file +} From 06334483335e6bdeb5d54ef2f066ee91867c36fa Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 15 Jul 2024 17:04:08 +0200 Subject: [PATCH 134/212] updates --- .../methods/kmeans/hamerly_kmeans_impl.hpp | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index a0992ddced..42d01e6060 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -80,7 +80,6 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // First bound test. if (upperBounds(i) <= m) { - #pragma omp atomic ++hamerlyPruned; #pragma omp critical { @@ -110,7 +109,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // This is Hamerly's Point-All-Ctrs() function from the paper. // We have to reset the lower bound first. lowerBounds(i) = DBL_MAX; - for (size_t c = 0; c < centroids.n_cols; ++c) + for (size_t c = 0; c < centroids.n_cols; ++c) { if (c == assignments[i]) continue; @@ -150,7 +149,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, double centroidMovement = 0.0; #pragma omp parallel for reduction(+:distanceCalculations, centroidMovement) \ - reduction(max:furthestMovement, secondFurthestMovement) + reduction(max:furthestMovement, secondFurthestMovement) for (size_t c = 0; c < centroids.n_cols; ++c) { if (counts(c) > 0) @@ -165,9 +164,19 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, if (movement > furthestMovement) { - secondFurthestMovement = furthestMovement; - furthestMovement = movement; - furthestMovingCluster = c; + #pragma omp critical + { + if (movement > furthestMovement) + { + secondFurthestMovement = furthestMovement; + furthestMovement = movement; + furthestMovingCluster = c; + } + else if (movement > secondFurthestMovement) + { + secondFurthestMovement = movement; + } + } } else if (movement > secondFurthestMovement) { From a1674c52be18d98f6d437d655c06f28a774aab07 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 15 Jul 2024 17:04:08 +0200 Subject: [PATCH 135/212] updates --- .../methods/kmeans/hamerly_kmeans_impl.hpp | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index cbf5d7857c..cc4e7bb976 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -79,7 +79,6 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // First bound test. if (upperBounds(i) <= m) { - #pragma omp atomic ++hamerlyPruned; #pragma omp critical { @@ -109,7 +108,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // This is Hamerly's Point-All-Ctrs() function from the paper. // We have to reset the lower bound first. lowerBounds(i) = DBL_MAX; - for (size_t c = 0; c < centroids.n_cols; ++c) + for (size_t c = 0; c < centroids.n_cols; ++c) { if (c == assignments[i]) continue; @@ -149,7 +148,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, double centroidMovement = 0.0; #pragma omp parallel for reduction(+:distanceCalculations, centroidMovement) \ - reduction(max:furthestMovement, secondFurthestMovement) + reduction(max:furthestMovement, secondFurthestMovement) for (size_t c = 0; c < centroids.n_cols; ++c) { if (counts(c) > 0) @@ -164,9 +163,19 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, if (movement > furthestMovement) { - secondFurthestMovement = furthestMovement; - furthestMovement = movement; - furthestMovingCluster = c; + #pragma omp critical + { + if (movement > furthestMovement) + { + secondFurthestMovement = furthestMovement; + furthestMovement = movement; + furthestMovingCluster = c; + } + else if (movement > secondFurthestMovement) + { + secondFurthestMovement = movement; + } + } } else if (movement > secondFurthestMovement) { From 713967355eb1b26194e31cd64f995f49091573d8 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 15 Jul 2024 17:27:39 +0200 Subject: [PATCH 136/212] removed space --- src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index cc4e7bb976..80efef786b 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -108,7 +108,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // This is Hamerly's Point-All-Ctrs() function from the paper. // We have to reset the lower bound first. lowerBounds(i) = DBL_MAX; - for (size_t c = 0; c < centroids.n_cols; ++c) + for (size_t c = 0; c < centroids.n_cols; ++c) { if (c == assignments[i]) continue; From 8df119338b50f9af42f76e56b8f194486dd23ca3 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 15 Jul 2024 18:30:24 +0200 Subject: [PATCH 137/212] removed fixed block size --- .../methods/kmeans/naive_kmeans_impl.hpp | 105 +++++++++--------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 8d789c2b80..e73c4eb532 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -50,66 +50,67 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, centroidNorms(j) = arma::dot(centroids.col(j), centroids.col(j)); } - // Block-wise for a better cache utilization - const size_t blockSize = 256; - - #pragma omp parallel + // Determine the number of threads and calculate segment size + const size_t numThreads = static_cast(std::max(1, omp_get_max_threads())); + const size_t minVectorsPerThread = 100; + const size_t effectiveThreads = std::min(numThreads, points / minVectorsPerThread); + const size_t nominalSegmentSize = points / effectiveThreads; + + #pragma omp parallel num_threads(effectiveThreads) { arma::mat localCentroids(dims, clusters, arma::fill::zeros); arma::Col localCounts(clusters, arma::fill::zeros); - #pragma omp for schedule(dynamic, 1) - for (size_t block = 0; block < points; block += blockSize) + const size_t threadId = omp_get_thread_num(); + const size_t segmentStart = threadId * nominalSegmentSize; + const size_t segmentEnd = (threadId == effectiveThreads - 1) ? points : (threadId + 1) * nominalSegmentSize; + + for (size_t i = segmentStart; i < segmentEnd; ++i) { - const size_t blockEnd = std::min(block + blockSize, points); + double minDistance = std::numeric_limits::max(); + size_t closestCluster = clusters; + + const double* dataPoint = dataset.colptr(i); + double dataNorm = 0.0; - for (size_t i = block; i < blockEnd; ++i) + // Compute data point norm + #pragma omp simd reduction(+:dataNorm) + for (size_t d = 0; d < dims; ++d) { - double minDistance = std::numeric_limits::max(); - size_t closestCluster = clusters; - - const double* dataPoint = dataset.colptr(i); - double dataNorm = 0.0; - - // Compute data point norm - #pragma omp simd reduction(+:dataNorm) - for (size_t d = 0; d < dims; ++d) - { - dataNorm += dataPoint[d] * dataPoint[d]; - } - - // Find closest centroid - for (size_t j = 0; j < clusters; ++j) - { - const double* centroid = centroids.colptr(j); - double dotProduct = 0.0; - - // Compute dot product - #pragma omp simd reduction(+:dotProduct) - for (size_t d = 0; d < dims; ++d) - { - dotProduct += dataPoint[d] * centroid[d]; - } - - // Use squared Euclidean distance - double dist = dataNorm + centroidNorms(j) - 2 * dotProduct; - - if (dist < minDistance) - { - minDistance = dist; - closestCluster = j; - } - } - - // Update local centroids and counts - double* localCentroidCol = localCentroids.colptr(closestCluster); - #pragma omp simd - for (size_t d = 0; d < dims; ++d) - { - localCentroidCol[d] += dataPoint[d]; - } - localCounts(closestCluster)++; + dataNorm += dataPoint[d] * dataPoint[d]; } + + // Find closest centroid + for (size_t j = 0; j < clusters; ++j) + { + const double* centroid = centroids.colptr(j); + double dotProduct = 0.0; + + // Compute dot product + #pragma omp simd reduction(+:dotProduct) + for (size_t d = 0; d < dims; ++d) + { + dotProduct += dataPoint[d] * centroid[d]; + } + + // Squared Euclidean distance + double dist = dataNorm + centroidNorms(j) - 2 * dotProduct; + + if (dist < minDistance) + { + minDistance = dist; + closestCluster = j; + } + } + + // Update local centroids and counts + double* localCentroidCol = localCentroids.colptr(closestCluster); + #pragma omp simd + for (size_t d = 0; d < dims; ++d) + { + localCentroidCol[d] += dataPoint[d]; + } + localCounts(closestCluster)++; } // Combine results From 96946087fdcc603fdf169c8176a2c3f2e4e8f21d Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Wed, 17 Jul 2024 19:25:17 +0530 Subject: [PATCH 138/212] minor fix --- .../decision_tree/fitness_functions/fitness_functions.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mlpack/methods/decision_tree/fitness_functions/fitness_functions.hpp b/src/mlpack/methods/decision_tree/fitness_functions/fitness_functions.hpp index a73fcd19c6..2ee82c29ca 100644 --- a/src/mlpack/methods/decision_tree/fitness_functions/fitness_functions.hpp +++ b/src/mlpack/methods/decision_tree/fitness_functions/fitness_functions.hpp @@ -1,5 +1,4 @@ #include "gini_gain.hpp" #include "information_gain.hpp" #include "mad_gain.hpp" -#include "mse_gain.hpp" -#include "sse_gain.hpp" \ No newline at end of file +#include "mse_gain.hpp" \ No newline at end of file From 3943dbeabd7d76f4b80c5f643703261a4d1e6459 Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Wed, 17 Jul 2024 19:42:25 +0530 Subject: [PATCH 139/212] minor fix --- .../decision_tree/split_functions/best_binary_numeric_split.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split.hpp b/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split.hpp index 218b4d1408..79c18ee63f 100644 --- a/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split.hpp +++ b/src/mlpack/methods/decision_tree/split_functions/best_binary_numeric_split.hpp @@ -13,7 +13,7 @@ #define MLPACK_METHODS_DECISION_TREE_BEST_BINARY_NUMERIC_SPLIT_HPP #include -#include +#include #include From c2742c13f15a7b47745d00ec1d1915aea6c35385 Mon Sep 17 00:00:00 2001 From: Abhimanyu Dayal Date: Wed, 17 Jul 2024 19:59:25 +0530 Subject: [PATCH 140/212] minor fix --- src/mlpack/methods/decision_tree/decision_tree.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/decision_tree/decision_tree.hpp b/src/mlpack/methods/decision_tree/decision_tree.hpp index 244cdec24e..62979e95ac 100644 --- a/src/mlpack/methods/decision_tree/decision_tree.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree.hpp @@ -605,7 +605,7 @@ typedef DecisionTree Date: Wed, 17 Jul 2024 20:04:27 +0530 Subject: [PATCH 141/212] removed xgbtree --- src/mlpack/methods/decision_tree/decision_tree.hpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/mlpack/methods/decision_tree/decision_tree.hpp b/src/mlpack/methods/decision_tree/decision_tree.hpp index 62979e95ac..eec026c92f 100644 --- a/src/mlpack/methods/decision_tree/decision_tree.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree.hpp @@ -601,15 +601,6 @@ typedef DecisionTree ID3DecisionStump; - -/** - * Convenience typedef for XGBoost trees. - */ -typedef DecisionTree XGBTree; } // namespace mlpack // Include implementation. From 3ea5618e947e36b788433380d4fa558eb876cfce Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 17 Jul 2024 16:06:25 -0400 Subject: [PATCH 142/212] Make some fixes so that 5000 runs of the distribution tests don't fail. --- src/mlpack/tests/distribution_test.cpp | 38 +++++++++++++------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/mlpack/tests/distribution_test.cpp b/src/mlpack/tests/distribution_test.cpp index 78dab01161..0862da6c55 100644 --- a/src/mlpack/tests/distribution_test.cpp +++ b/src/mlpack/tests/distribution_test.cpp @@ -458,23 +458,23 @@ TEST_CASE("GaussianDistributionRandomTest", "[DistributionTest]") GaussianDistribution d(mean, cov); - arma::mat obs(2, 5000); + arma::mat obs(2, 7500); - for (size_t i = 0; i < 5000; ++i) + for (size_t i = 0; i < 7500; ++i) obs.col(i) = d.Random(); // Now make sure that reflects the actual distribution. arma::vec obsMean = arma::mean(obs, 1); arma::mat obsCov = ColumnCovariance(obs); - // 10% tolerance because this can be noisy. - REQUIRE(obsMean[0] == Approx(mean[0]).epsilon(0.1)); - REQUIRE(obsMean[1] == Approx(mean[1]).epsilon(0.1)); + // 12.5% tolerance because this can be noisy. + REQUIRE(obsMean[0] == Approx(mean[0]).epsilon(0.125)); + REQUIRE(obsMean[1] == Approx(mean[1]).epsilon(0.125)); - REQUIRE(obsCov(0, 0) == Approx(cov(0, 0)).epsilon(0.1)); - REQUIRE(obsCov(0, 1) == Approx(cov(0, 1)).epsilon(0.1)); - REQUIRE(obsCov(1, 0) == Approx(cov(1, 0)).epsilon(0.1)); - REQUIRE(obsCov(1, 1) == Approx(cov(1, 1)).epsilon(0.1)); + REQUIRE(obsCov(0, 0) == Approx(cov(0, 0)).epsilon(0.125)); + REQUIRE(obsCov(0, 1) == Approx(cov(0, 1)).epsilon(0.125)); + REQUIRE(obsCov(1, 0) == Approx(cov(1, 0)).epsilon(0.125)); + REQUIRE(obsCov(1, 1) == Approx(cov(1, 1)).epsilon(0.125)); } /** @@ -527,7 +527,7 @@ TEST_CASE("GaussianDistributionTrainWithProbabilitiesTest", arma::vec cov = ("2.0"); GaussianDistribution dist(mean, cov); - size_t N = 5000; + size_t N = 15000; size_t d = 1; arma::mat rdata(d, N); @@ -548,10 +548,10 @@ TEST_CASE("GaussianDistributionTrainWithProbabilitiesTest", REQUIRE(guDist.Mean()[0] == Approx(guDist2.Mean()[0]).epsilon(0.06)); REQUIRE(guDist.Covariance()[0] == - Approx(guDist2.Covariance()[0]).epsilon(0.06)); + Approx(guDist2.Covariance()[0]).epsilon(0.08)); REQUIRE(guDist.Mean()[0] == Approx(mean[0]).epsilon(0.06)); - REQUIRE(guDist.Covariance()[0] == Approx(cov[0]).epsilon(0.06)); + REQUIRE(guDist.Covariance()[0] == Approx(cov[0]).epsilon(0.08)); } /** @@ -819,11 +819,11 @@ TEST_CASE("GammaDistributionTrainTwoDistProbabilities1Test", GammaDistribution gDist; gDist.Train(rdata, probabilities); - REQUIRE(alphaReal2 == Approx(gDist.Alpha(0)).epsilon(0.05)); - REQUIRE(betaReal2 == Approx(gDist.Beta(0)).epsilon(0.05)); + REQUIRE(alphaReal2 == Approx(gDist.Alpha(0)).epsilon(0.075)); + REQUIRE(betaReal2 == Approx(gDist.Beta(0)).epsilon(0.075)); - REQUIRE(alphaReal2 == Approx(gDist.Alpha(1)).epsilon(0.05)); - REQUIRE(betaReal2 == Approx(gDist.Beta(1)).epsilon(0.05)); + REQUIRE(alphaReal2 == Approx(gDist.Alpha(1)).epsilon(0.075)); + REQUIRE(betaReal2 == Approx(gDist.Beta(1)).epsilon(0.075)); } /** @@ -933,7 +933,7 @@ TEST_CASE("GammaDistributionTrainStatisticsTest", "[DistributionTest]") TEST_CASE("GammaDistributionRandomTest", "[DistributionTest]") { const arma::vec a("2.0 2.5 3.0"), b("0.4 0.6 1.3"); - const size_t numPoints = 2000; + const size_t numPoints = 4000; // Distribution to generate points. GammaDistribution d1(a, b); @@ -946,8 +946,8 @@ TEST_CASE("GammaDistributionRandomTest", "[DistributionTest]") GammaDistribution d2(data); for (size_t i = 0; i < 3; ++i) { - REQUIRE(d2.Alpha(i) == Approx(a(i)).epsilon(0.1)); // Within 10% - REQUIRE(d2.Beta(i) == Approx(b(i)).epsilon(0.1)); + REQUIRE(d2.Alpha(i) == Approx(a(i)).epsilon(0.15)); // Within 15% + REQUIRE(d2.Beta(i) == Approx(b(i)).epsilon(0.15)); } } From 0c9702cb6415c313cb66abdaddca6110c9ec2c41 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 17 Jul 2024 22:06:06 -0400 Subject: [PATCH 143/212] Finish MeanShift documentation and debug tests. --- doc/user/core.md | 15 +- doc/user/methods/decision_tree.md | 5 +- doc/user/methods/decision_tree_regressor.md | 3 - doc/user/methods/mean_shift.md | 226 +++++++++++++++++- src/mlpack/methods/mean_shift/mean_shift.hpp | 34 ++- .../methods/mean_shift/mean_shift_impl.hpp | 57 +++-- src/mlpack/tests/mean_shift_test.cpp | 91 +++++++ 7 files changed, 384 insertions(+), 47 deletions(-) diff --git a/doc/user/core.md b/doc/user/core.md index 0caaea7459..4a81258ac7 100644 --- a/doc/user/core.md +++ b/doc/user/core.md @@ -1641,7 +1641,8 @@ where `bw` is the bandwidth parameter of the kernel. * `g.Gradient(distance)` - Compute the (one-dimensional) gradient of the kernel function with respect - to the distance between two points, evaluated at `distance`. + to the distance between two points, evaluated at `distance`. This is used + by [`MeanShift`](methods/mean_shift.md). * `g.Normalizer(dimensionality)` - Return the @@ -1876,7 +1877,8 @@ distance between `x1` and `x2`) is greater than or equal to `bw`. * `e.Gradient(distance)` - Compute the (one-dimensional) gradient of the kernel function with respect - to the distance between two points, evaluated at `distance`. + to the distance between two points, evaluated at `distance`. This is used + by [`MeanShift`](methods/mean_shift.md). * `e.Normalizer(dimensionality)` - Return the @@ -2046,7 +2048,8 @@ where `bw` is the bandwidth parameter. * `l.Gradient(distance)` - Compute the (one-dimensional) gradient of the kernel function with respect - to the distance between two points, evaluated at `distance`. + to the distance between two points, evaluated at `distance`. This is used + by [`MeanShift`](methods/mean_shift.md). --- @@ -2365,7 +2368,8 @@ as the uniform kernel, or rectangular window kernel. The value of the * `s.Gradient(distance)` - Compute the (one-dimensional) gradient of the kernel function with respect - to the distance between two points, evaluated at `distance`. + to the distance between two points, evaluated at `distance`. This is used + by [`MeanShift`](methods/mean_shift.md). * `s.Normalizer(dimensionality)` - Return the @@ -2459,7 +2463,8 @@ where `bw` is the bandwidth of the kernel. * `t.Gradient(distance)` - Compute the (one-dimensional) gradient of the kernel function with respect - to the distance between two points, evaluated at `distance`. + to the distance between two points, evaluated at `distance`. This is used + by [`MeanShift`](methods/mean_shift.md). --- diff --git a/doc/user/methods/decision_tree.md b/doc/user/methods/decision_tree.md index f4b4253b9e..cec361d871 100644 --- a/doc/user/methods/decision_tree.md +++ b/doc/user/methods/decision_tree.md @@ -458,11 +458,9 @@ class CustomNumericSplit categorical feature. * The `AllCategoricalSplit` _(default)_ and `BestBinaryCategoricalSplit` are~ available for drop-in usage. - * `AllCategoricalSplit`, the default ID3 split algorithm, splits all categories into their own node. This variant is simple, and has complexity `O(n)`, where `n` is the number of samples. - * `BestBinaryCategoricalSplit` is the preferred algorithm of [the CART system](https://www.taylorfrancis.com/books/mono/10.1201/9781315139470/classification-regression-trees-leo-breiman-jerome-friedman-olshen-charles-stone). It will find the the best (entropy-minimizing) binary partition of the @@ -471,6 +469,9 @@ class CustomNumericSplit more than two _classes_.~ - ***Note***: `BestBinaryCategoricalSplit` should not be chosen when there are multiple classes and many categories. + * A custom class must take a [`FitnessFunction`](#fitnessfunction) as a + template parameter, implement three functions, and have an internal + structure `AuxiliarySplitInfo` that is used at classification time: ```c++ template diff --git a/doc/user/methods/decision_tree_regressor.md b/doc/user/methods/decision_tree_regressor.md index f484b30ce1..b0a0cb8c21 100644 --- a/doc/user/methods/decision_tree_regressor.md +++ b/doc/user/methods/decision_tree_regressor.md @@ -462,11 +462,9 @@ class CustomNumericSplit categorical feature. * The `AllCategoricalSplit` _(default)_ and `BestBinaryCategoricalSplit` are~ available for drop-in usage. - * `AllCategoricalSplit`, the default ID3 split algorithm, splits all categories into their own node. This variant is simple, and has complexity `O(n)`, where `n` is the number of samples. - * `BestBinaryCategoricalSplit` is the preferred algorithm of [the CART system](https://www.taylorfrancis.com/books/mono/10.1201/9781315139470/classification-regression-trees-leo-breiman-jerome-friedman-olshen-charles-stone). It will find the the best (entropy-minimizing) binary partition of the @@ -479,7 +477,6 @@ class CustomNumericSplit [W. Fisher's proof of correctness](http://www.csiss.org/SPACE/workshops/2004/SAC/files/fisher.pdf) only applies to when `FitnessFunction` is `MSEGain`; therefore, `BestBinaryCategoricalSplit` requires the use of `MSEGain`. - * A custom class must take a [`FitnessFunction`](#fitnessfunction) as a template parameter, implement three functions, and have an internal structure `AuxiliarySplitInfo` that is used at classification time: diff --git a/doc/user/methods/mean_shift.md b/doc/user/methods/mean_shift.md index 9f0d1b504b..9d9fcdda15 100644 --- a/doc/user/methods/mean_shift.md +++ b/doc/user/methods/mean_shift.md @@ -15,9 +15,10 @@ template parameters. // Use mean shift to cluster random data and print the number of points that // fall into each cluster. -// All data is uniform random 10-dimensional; replace with a data::Load() call -// or similar for a real application. -arma::mat dataset(10, 1000, arma::fill::randu); +// Create random dataset with two separated 10-dimensional Gaussians. +arma::mat dataset = arma::join_rows( + arma::randn(10, 1000) + 3.0, // 1000 points from N(-3, 1). + arma::randn(10, 1000) - 3.0); // 1000 points from N( 3, 1). mlpack::MeanShift ms; // Step 1: create object. arma::Row assignments; @@ -39,7 +40,7 @@ for (size_t c = 0; c < centroids.n_cols; ++c) #### Quick links: * [Constructors](#constructors): create `MeanShift` objects. - * [`Cluster()`](#cluster): perform clustering. + * [`Cluster()`](#clustering): perform clustering. * [Other functionality](#other-functionality) for loading, saving, inspecting, and estimating the radius to use. * [Examples](#simple-examples) of simple usage and links to detailed example @@ -57,37 +58,119 @@ for (size_t c = 0; c < centroids.n_cols; ++c) ### Constructors * `ms = MeanShift(radius=0, maxIterations=1000)` + - Create a `MeanShift` object that will use the + default [`GaussianKernel`](../core.md#gaussiankernel) to weight points for + cluster centroid recalculations. --- * `ms = MeanShift(radius=0, maxIterations=1000)` + - Create a `MeanShift` object that will not weight points when recalculating + cluster centroids. --- * `ms = MeanShift(radius, maxIterations, kernel)` + - Create a `MeanShift` object that will use the given `kernel` object (a + `GaussianKernel`) for weighting points during cluster centroid + recalculations. --- - * `ms = MeanShift(radius, maxIterations, kernel)` + * `ms = MeanShift(radius, maxIterations, kernel=KernelType())` + - Create a `MeanShift` object that will use the given + [`KernelType`](../core.md#kernels) for weighting points during cluster + centroid recalculations. + - Any [mlpack kernel](../core.md#kernels) or custom kernel class implementing + a [`Gradient()` function](#advanced-functionality-template-parameters) can + be used for the `KernelType` template parameter. + - If `kernel` is not specified, a default-constructed `KernelType` will be + used. --- +#### Constructor Parameters: + +| **name** | **type** | **description** | **default** | +|----------|----------|-----------------|-------------| +| `radius` | `double` | Radius around each centroid for weighting during centroid recomputation. Larger means higher weights for faraway points. Values less than or equal to 0 mean that the radius will be estimated from data. | `0.0` | +| `maxIterations` | `size_t` | Maximum number of iterations of the mean shift algorithm to run. | `1000` | +| `kernel` | [`KernelType`](#advanced-functionality-template-parameters) (default `GaussianKernel`) | Instantiated kernel object to use for density calculations. | `KernelType()` | + +***Notes:*** + + - A larger `radius` value will generally result in fewer clusters (e.g. a + coarser clustering); smaller `radius` values will generally result in more + clusters. + + - When `MeanShift` is used, `radius` is the hard distance threshold for + points to be considered in the recomputation of a centroid. + ### Clustering * `ms.Cluster(data, centroids, forceConvergence=true, useSeeds=true)` + - Cluster the given data, storing the resulting cluster centroids in + `centroids`. + - `centroids` will be set to size `data.n_rows` x `numClusters`, where + `numClusters` is the number of clusters found by the mean shift algorithm. + - The `i`th cluster centroid can be obtained with `clusters.col(i)`. --- * `ms.Cluster(data, assignments, centroids, forceConvergence=true, useSeeds=true)` + - Cluster the given data, storing the resulting cluster centroids in + `centroids` and cluster assignments for each data point in `assignments`. + - `centroids` will be set to size `data.n_rows` x `numClusters`, where + `numClusters` is the number of clusters found by the mean shift algorithm. + - `assignments` will be set to length `data.n_cols`; the assignment of the + `i`th point can be obtained with `assignments[i]`. + - The cluster centroid of the `i`th point's cluster can be obtained with + `centroids.col(assignments[i])`. --- +#### Clustering Parameters: + +| **name** | **type** | **description** | **default** | +|----------|----------|-----------------|-------------| +| `data` | [`arma::mat`](../matrices.md) | [Column-major](../matrices.md#representing-data-in-mlpack) matrix holding the dataset to be clustered. | _(N/A)_ | +| `centroids` | [`arma::mat`](../matrices.md) | [Column-major](../matrices.md#representing-data-in-mlpack) matrix that centroids will be stored into. | _(N/A)_ | +| `assignments` | [`arma::Row`](../matrices.md) | Vector to store cluster assignments for each point into. | _(N/A)_ | +| `forceConvergence` | `bool` | If `true`, forces convergence of every cluster, ignoring `maxIterations`. | `false` | +| `useSeeds` | `bool` | If `true`, estimates of high-density regions in the +dataset will be used as initial centroids, instead of the full dataset. | `true` + +***Notes***: + + * It is recommended to leave `useSeeds` to its default value of `true`. When + `useSeeds` is set to `false`, the entire dataset is used as the initial set + of centroids. For large datasets, this can be slow! + + * Different types can be used for `data` and `centroids` (e.g., `arma::fmat` or + any dense matrix type implementing the Armadillo API). The types of `data` + and `centroids` must be the same. + ### Other Functionality * A `MeanShift` object can be serialized with [`data::Save()` and `data::Load()`](../load_save.md#mlpack-objects). - * `EstimateRadius()` + * `EstimateRadius(data, ratio=0.2)` returns a `double` that estimates a good + value to use for the radius parameter. `ratio` (between 0 and 1) controls + the percentage of the dataset used for the estimate. + - This function is called internally by `Cluster()` at the start of + clustering to choose a radius, if `radius` is less than or equal to 0. + + * As an alternative to constructor parameters, the radius can be set with + `ms.Radius(newRadius)`, and the maximum number of iterations can be set with + `ms.MaxIterations() = newMaxIter`. + + * `ms.Radius()` returns the current radius for clustering. + `ms.Radius(r)` sets the radius to `r`. + + * `ms.MaxIterations()` returns the current maximum number of iterations for + clustering. `ms.MaxIterations() = m` sets the maximum number of iterations + to `m`. ### Simple Examples @@ -95,16 +178,62 @@ Perform mean shift clustering on the satellite dataset and print the average distance from each point to its assigned centroid. ```c++ +// See https://datasets.mlpack.org/satellite.train.csv. +arma::mat dataset; +mlpack::data::Load("satellite.train.csv", dataset, true); +// Create MeanShift object with default parameters and perform clustering. +mlpack::MeanShift ms; +arma::mat centroids; +arma::Row assignments; +ms.Cluster(dataset, assignments, centroids); + +// Print the number of clusters. +std::cout << "MeanShift computed " << centroids.n_cols << " clusters." + << std::endl; + +// Compute the average distance from each point to its assigned centroid. +double sumDist = 0.0; +for (size_t i = 0; i < dataset.n_cols; ++i) +{ + sumDist += mlpack::EuclideanDistance::Evaluate(dataset.col(i), + centroids.col(assignments[i])); +} +const double avgDist = sumDist / (double) dataset.n_cols; + +std::cout << "Average distance from a point to its assigned centroid: " + << avgDist << "." << std::endl; ``` --- Perform mean shift clustering with custom settings of `radius` and -`maxIterations` on a subset of the covertype dataset, using `EstimateRadius()` +`maxIterations` on the wave energy farm dataset, using `EstimateRadius()` to set the initial radius. ```c++ +// See https://datasets.mlpack.org/wave_energy_farm_100.csv. +arma::mat dataset; +mlpack::data::Load("wave_energy_farm_100.csv", dataset, true); + +// Create MeanShift object and set parameters. +mlpack::MeanShift ms; +const double radiusEstimate = ms.EstimateRadius(dataset, 0.2); + +// Use 2x the estimate for a coarser clustering. +ms.Radius(2.0 * radiusEstimate); +// Use only 100 iterations. +ms.MaxIterations() = 100; + +// Perform the clustering. +arma::mat centroids; +ms.Cluster(dataset, centroids); + +std::cout << "MeanShift found " << centroids.n_cols << " clusters." + << std::endl; + +// Save the centroids to disk. +mlpack::data::Save("wave_energy_centroids.csv", centroids); ``` --- @@ -113,7 +242,26 @@ Perform mean shift clustering with no kernel (e.g. unit weighting of points in a centroid) on the cloud dataset. ```c++ +// See https://datasets.mlpack.org/cloud.csv. +arma::mat dataset; +mlpack::data::Load("cloud.csv", dataset, true); +// Don't use a kernel for clustering. This means all points within the radius +// are weighted equally. Use a custom radius of 25. +mlpack::MeanShift ms(25.0, 100 /* max iterations */); + +arma::mat centroids; +arma::Row assignments; +ms.Cluster(dataset, assignments, centroids); + +// Print the number of clusters and the number of points in each cluster. +std::cout << "MeanShift found " << centroids.n_cols << " clusters." + << std::endl; +for (size_t i = 0; i < centroids.n_cols; ++i) +{ + std::cout << " - Cluster " << i << " has " << arma::accu(assignments == i) + << " points assigned to it." << std::endl; +} ``` --- @@ -122,17 +270,69 @@ Perform mean shift clustering with the triangular kernel on the cloud dataset, using 32-bit floating point matrices to represent the data. ```c++ +// See https://datasets.mlpack.org/cloud.csv. +arma::fmat dataset; +mlpack::data::Load("cloud.csv", dataset, true); -``` +// Create the MeanShift object using a TriangularKernel. +mlpack::TriangularKernel tk; +mlpack::MeanShift ms(50.0 /* radius */, + 1000 /* max iterations */, + tk); ---- - -Perform mean shift clustering on a random sparse dataset. - -```c++ +// Perform clustering. +arma::fmat centroids; +arma::Row assignments; +ms.Cluster(dataset, assignments, centroids); +// Print the number of clusters and the number of points in each cluster. +std::cout << "MeanShift found " << centroids.n_cols << " clusters." + << std::endl; +for (size_t i = 0; i < centroids.n_cols; ++i) +{ + std::cout << " - Cluster " << i << " has " << arma::accu(assignments == i) + << " points assigned to it." << std::endl; +} ``` --- ### Advanced Functionality: Template Parameters + +The `MeanShift` class has two template parameters that can be used for custom +behavior. The full signature of the class is: + +``` +MeanShift +``` + + * `UseKernel` (default `true`) is a `bool` parameter representing whether a + kernel function is used to weight points during centroid recomputation. If + it is `false`, then each point within distance `radius` of the centroid will + be used (without weighting) to recompute the centroid. This strategy (with + `UseKernel = false`) is also known as using a 'flat kernel'. + + * `KernelType` represents the kernel function (or Parzen window) to be used to + weight points during centroid recomputation. Although many + [mlpack kernels](../core.md#kernels) are available, only those with + `Gradient()` functions (described below) are supported. Available kernels + for drop-in usage include: + - [`GaussianKernel`](../core.md#gaussiankernel) *(default)* + - [`EpanechnikovKernel`](../core.md#epanechnikovkernel) + - [`LaplacianKernel`](../core.md#laplaciankernel) + - [`SphericalKernel`](../core.md#sphericalkernel) *(note: this is equivalent + to the flat kernel, or, setting `UseKernel = false`)* + - [`TriangularKernel`](../core.md#triangularkernel) + +Custom kernels can be easily implemented, and must implement only one function +(`Gradient()`): + +```c++ +class CustomKernel +{ + // Evaluate the gradient of the kernel function given the distance between two + // points. Specifically, given that the kernel function is K(t) (where t is + // the distance between the two points), this function should return K'(t). + double Gradient(const double t); +}; +``` diff --git a/src/mlpack/methods/mean_shift/mean_shift.hpp b/src/mlpack/methods/mean_shift/mean_shift.hpp index 8362936241..6746728a15 100644 --- a/src/mlpack/methods/mean_shift/mean_shift.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift.hpp @@ -70,23 +70,45 @@ class MeanShift typename MatType::elem_type EstimateRadius(const MatType& data, const double ratio = 0.2); + /** + * Perform mean shift clusteirng on the data, returning a list of centroids. + * + * @tparam MatType Type of matrix. + * @tparam LabelsType Type of labels (should be similar to arma::Row). + * @tparam CentroidsType Type of matrix to store centroids in; should have + * same element type as MatType. + * @param data Dataset to cluster. + * @param centroids Matrix in which centroids are stored. + * @param forceConvergence Flag whether to force each centroid seed to + * converge regardless of maxIterations. + * @param useSeeds Set true to use seeds. + */ + template + void Cluster(const MatType& data, + CentroidsType& centroids, + bool forceConvergence = false, + bool useSeeds = true); + /** * Perform mean shift clustering on the data, returning a list of cluster * assignments and centroids. * * @tparam MatType Type of matrix. + * @tparam LabelsType Type of labels (should be similar to arma::Row). + * @tparam CentroidsType Type of matrix to store centroids in; should have + * same element type as MatType. * @param data Dataset to cluster. * @param assignments Vector to store cluster assignments in. * @param centroids Matrix in which centroids are stored. * @param forceConvergence Flag whether to force each centroid seed to - * converge regardless of maxIterations. + * converge regardless of maxIterations. * @param useSeeds Set true to use seeds. */ - template + template void Cluster(const MatType& data, LabelsType& assignments, - MatType& centroids, - bool forceConvergence = true, + CentroidsType& centroids, + bool forceConvergence = false, bool useSeeds = true); //! Get the maximum number of iterations. @@ -118,11 +140,11 @@ class MeanShift * @param minFreq Minimum number of points in bin. * @param seed Matrix to store generated seeds in. */ - template + template void GenSeeds(const MatType& data, const double binSize, const int minFreq, - MatType& seeds); + CentroidsType& seeds); /** * Use kernel to calculate new centroid given dataset and valid neighbors. diff --git a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp index 1ca15d026c..e3ea0dd09e 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp @@ -88,13 +88,14 @@ class less // Generate seeds from given data set. template -template +template void MeanShift::GenSeeds(const MatType& data, const double binSize, const int minFreq, - MatType& seeds) + CentroidsType& seeds) { typedef typename GetColType::type VecType; + typedef typename GetColType::type CentroidVecType; std::map > allSeeds; for (size_t i = 0; i < data.n_cols; ++i) { @@ -119,7 +120,7 @@ void MeanShift::GenSeeds(const MatType& data, { if (it->second >= minFreq) { - seeds.col(count) = it->first; + seeds.col(count) = arma::conv_to::from(it->first); ++count; } } @@ -177,15 +178,13 @@ MeanShift::CalculateCentroid( } /** - * Perform Mean Shift clustering on the data set, returning a list of cluster - * assignments and centroids. + * Perform Mean Shift clustering on the data set, returning a list of centroids. */ template -template +template inline void MeanShift::Cluster( const MatType& data, - LabelsType& assignments, - MatType& centroids, + CentroidsType& centroids, bool forceConvergence, bool useSeeds) { @@ -199,7 +198,7 @@ inline void MeanShift::Cluster( Radius(EstimateRadius(data)); } - MatType seeds; + CentroidsType seeds; const MatType* pSeeds = &data; if (useSeeds) { @@ -208,14 +207,12 @@ inline void MeanShift::Cluster( } // Holds all centroids before removing duplicate ones. - MatType allCentroids(pSeeds->n_rows, pSeeds->n_cols); - - assignments.set_size(data.n_cols); + CentroidsType allCentroids(pSeeds->n_rows, pSeeds->n_cols); RangeSearch rangeSearcher(data); RangeType validRadius((ElemType) 0, (ElemType) radius); - std::vector > neighbors; - std::vector > distances; + std::vector> neighbors; + std::vector> distances; // For each seed, perform mean shift algorithm. for (size_t i = 0; i < pSeeds->n_cols; ++i) @@ -282,19 +279,43 @@ inline void MeanShift::Cluster( { centroids.insert_cols(centroids.n_cols, allCentroids.col(0)); } - assignments.zeros(); } - else if (centroids.n_cols == 1) +} + +/** + * Perform Mean Shift clustering on the data set, returning a list of cluster + * assignments and centroids. + */ +template +template +inline void MeanShift::Cluster( + const MatType& data, + LabelsType& assignments, + CentroidsType& centroids, + bool forceConvergence, + bool useSeeds) +{ + // Perform the actual clustering. + Cluster(data, centroids, forceConvergence, useSeeds); + + assignments.set_size(data.n_cols); + if (centroids.n_cols == 1) { assignments.zeros(); } else { // Assign centroids to each point. + // + // NeighborSearch only supports when the reference and query set have the + // same type, so forcibly convert the centroids to the same type as data if + // needed. This also means we have to separate out the neighbor searching + // operation to a utility function, so that the compiler doesn't try to + // instantiate the NeighborSearch class with invalid types. + arma::Mat neighborDistances; + arma::Mat resultingNeighbors; NeighborSearch neighborSearcher(centroids); - MatType neighborDistances; - arma::Mat resultingNeighbors; neighborSearcher.Search(data, 1, resultingNeighbors, neighborDistances); assignments = resultingNeighbors; } diff --git a/src/mlpack/tests/mean_shift_test.cpp b/src/mlpack/tests/mean_shift_test.cpp index 52a52c548a..2d319acd6a 100644 --- a/src/mlpack/tests/mean_shift_test.cpp +++ b/src/mlpack/tests/mean_shift_test.cpp @@ -92,6 +92,19 @@ TEMPLATE_TEST_CASE("MeanShiftSimpleTest", "[MeanShiftTest]", float, double) REQUIRE(assignments(i) == thirdClass); } +TEMPLATE_TEST_CASE("MeanShiftSimpleCentroidsOnlyTest", "[MeanShiftTest]", float, double) +{ + typedef TestType ElemType; + + MeanShift<> meanShift; + + arma::Mat centroids; + meanShift.Cluster(GetMeanShiftData>(), centroids); + + // Just check that the size is right. + REQUIRE(centroids.n_cols == 3); +} + // Generate samples from four Gaussians, and make sure mean shift nearly // recovers those four centers. TEMPLATE_TEST_CASE("GaussianClustering", "[MeanShiftTest]", float, double) @@ -172,3 +185,81 @@ TEMPLATE_TEST_CASE("GaussianClustering", "[MeanShiftTest]", float, double) REQUIRE(success == true); } + +TEMPLATE_TEST_CASE("GaussianClusteringCentroidsOnly", "[MeanShiftTest]", float, double) +{ + typedef TestType ElemType; + + GaussianDistribution g1("0.0 0.0 0.0", arma::eye(3, 3)); + GaussianDistribution g2("5.0 5.0 5.0", 2 * arma::eye(3, 3)); + GaussianDistribution g3("-3.0 3.0 -1.0", arma::eye(3, 3)); + GaussianDistribution g4("6.0 -2.0 -2.0", 3 * arma::eye(3, 3)); + + // We may need to run this multiple times, because sometimes it may converge + // to the wrong number of clusters. + bool success = false; + for (size_t trial = 0; trial < 4; ++trial) + { + arma::Mat dataset(3, 4000); + for (size_t i = 0; i < 1000; ++i) + dataset.col(i) = arma::conv_to>::from(g1.Random()); + for (size_t i = 1000; i < 2000; ++i) + dataset.col(i) = arma::conv_to>::from(g2.Random()); + for (size_t i = 2000; i < 3000; ++i) + dataset.col(i) = arma::conv_to>::from(g3.Random()); + for (size_t i = 3000; i < 4000; ++i) + dataset.col(i) = arma::conv_to>::from(g4.Random()); + + // Now that the dataset is generated, run mean shift. Pre-set radius. + MeanShift<> meanShift(2.9); + + arma::Mat centroids; + meanShift.Cluster(dataset, centroids); + + success = (centroids.n_cols == 4); + if (!success) + continue; + success = (centroids.n_rows == 3); + if (!success) + continue; + + // Check that each centroid is close to only one mean. + arma::Col centroidDistances(4); + arma::uvec minIndices(4); + for (size_t i = 0; i < 4; ++i) + { + centroidDistances(0) = EuclideanDistance::Evaluate( + arma::conv_to>::from(g1.Mean()), + centroids.col(i)); + centroidDistances(1) = EuclideanDistance::Evaluate( + arma::conv_to>::from(g2.Mean()), + centroids.col(i)); + centroidDistances(2) = EuclideanDistance::Evaluate( + arma::conv_to>::from(g3.Mean()), + centroids.col(i)); + centroidDistances(3) = EuclideanDistance::Evaluate( + arma::conv_to>::from(g4.Mean()), + centroids.col(i)); + + // Are we near a centroid of a Gaussian? + const ElemType minVal = centroidDistances.min(minIndices[i]); + success = (std::abs(minVal) <= 0.65); + if (!success) + break; + } + + // Ensure each centroid corresponds to a different Gaussian. + bool innerSuccess = true; + for (size_t i = 0; i < 4; ++i) + for (size_t j = i + 1; j < 4; ++j) + innerSuccess &= (minIndices[i] != minIndices[j]); + + if (innerSuccess) + success = true; + + if (success) + break; + } + + REQUIRE(success == true); +} From 01a16cefbf90ae7f25250acaccd2e94f62809d9a Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 17 Jul 2024 22:09:00 -0400 Subject: [PATCH 144/212] Update main documentation page and sidebar. --- doc/index.md | 3 ++- doc/sidebar.html | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/doc/index.md b/doc/index.md index 607a2ae214..59015a0a7a 100644 --- a/doc/index.md +++ b/doc/index.md @@ -102,7 +102,8 @@ Predict continuous values. Group points into clusters. - + * [`MeanShift`](user/methods/mean_shift.md): clustering with the density-based + mean shift algorithm ### Geometric algorithms diff --git a/doc/sidebar.html b/doc/sidebar.html index 568496310e..9d4bfb77c3 100644 --- a/doc/sidebar.html +++ b/doc/sidebar.html @@ -132,9 +132,20 @@ when the sidebar is built for each page.
  • - - Clustering - +
    + + + Clustering + + + +
  • From d565b5848ec17424dd848183623111f774fde866 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Thu, 18 Jul 2024 13:06:26 +0200 Subject: [PATCH 145/212] fix --- .../methods/kmeans/hamerly_kmeans_impl.hpp | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index 80efef786b..f69eb4c8e5 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -44,7 +44,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, minClusterDistances.set_size(centroids.n_cols); } - // Reset new centroids. + // Reset new centroids and counts. newCentroids.zeros(centroids.n_rows, centroids.n_cols); counts.zeros(centroids.n_cols); @@ -60,17 +60,15 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, ++distanceCalculations; // Update bounds, if this intra-cluster distance is smaller. - #pragma omp critical - { - if (dist < minClusterDistances(i)) - minClusterDistances(i) = dist; - if (dist < minClusterDistances(j)) - minClusterDistances(j) = dist; - } + #pragma omp atomic + minClusterDistances(i) = std::min(minClusterDistances(i), dist); + #pragma omp atomic + minClusterDistances(j) = std::min(minClusterDistances(j), dist); } } - #pragma omp parallel for reduction(+:distanceCalculations, hamerlyPruned) + #pragma omp parallel for reduction(+:distanceCalculations, hamerlyPruned) \ + reduction(+:newCentroids, counts) for (size_t i = 0; i < dataset.n_cols; ++i) { const double m = std::max(minClusterDistances(assignments[i]), @@ -80,11 +78,8 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, if (upperBounds(i) <= m) { ++hamerlyPruned; - #pragma omp critical - { - newCentroids.col(assignments[i]) += dataset.col(i); - ++counts(assignments[i]); - } + newCentroids.col(assignments[i]) += dataset.col(i); + ++counts(assignments[i]); continue; } @@ -96,11 +91,8 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // Second bound test. if (upperBounds(i) <= m) { - #pragma omp critical - { - newCentroids.col(assignments[i]) += dataset.col(i); - ++counts(assignments[i]); - } + newCentroids.col(assignments[i]) += dataset.col(i); + ++counts(assignments[i]); continue; } @@ -132,11 +124,8 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, distanceCalculations += centroids.n_cols - 1; // Update new centroids. - #pragma omp critical - { - newCentroids.col(assignments[i]) += dataset.col(i); - ++counts(assignments[i]); - } + newCentroids.col(assignments[i]) += dataset.col(i); + ++counts(assignments[i]); } // Normalize centroids and calculate cluster movement (contains parts of @@ -148,7 +137,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, double centroidMovement = 0.0; #pragma omp parallel for reduction(+:distanceCalculations, centroidMovement) \ - reduction(max:furthestMovement, secondFurthestMovement) + reduction(max:furthestMovement) for (size_t c = 0; c < centroids.n_cols; ++c) { if (counts(c) > 0) @@ -177,10 +166,6 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, } } } - else if (movement > secondFurthestMovement) - { - secondFurthestMovement = movement; - } } // Now update bounds (lines 3-8 of Update-Bounds()). From 2adac2b70bd3fa7190507869544ec362c685e70a Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 18 Jul 2024 09:23:02 -0400 Subject: [PATCH 146/212] Wording fixes and other minor changes after review. --- doc/user/methods/mean_shift.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/doc/user/methods/mean_shift.md b/doc/user/methods/mean_shift.md index 9d9fcdda15..e67ea48cef 100644 --- a/doc/user/methods/mean_shift.md +++ b/doc/user/methods/mean_shift.md @@ -1,10 +1,10 @@ ## `MeanShift` The `MeanShift` class implements mean shift, a clustering technique. Mean shift -models the density of the data using a specified kernel function, producing a -number of clusters that represent the data density. Mean shift does not require -the user to guess the number of clusters, and does not make any assumptions on -the shape of the data. +models the density of the data using a kernel function (also called Parzen +window), producing a number of clusters that represent the data density. Mean +shift does not require the user to guess the number of clusters, and does not +make any assumptions on the shape of the data. mlpack's `MeanShift` class allows control of the kernel function used via template parameters. @@ -65,8 +65,10 @@ for (size_t c = 0; c < centroids.n_cols; ++c) --- * `ms = MeanShift(radius=0, maxIterations=1000)` - - Create a `MeanShift` object that will not weight points when recalculating - cluster centroids. + - Create a `MeanShift` object that will not weight points differently when + recalculating cluster centroids. + - Centroid recalculation will use all points within a distance of `radius` + from the current cluster centroid, uniformly weighted. --- @@ -81,11 +83,13 @@ for (size_t c = 0; c < centroids.n_cols; ++c) - Create a `MeanShift` object that will use the given [`KernelType`](../core.md#kernels) for weighting points during cluster centroid recalculations. - - Any [mlpack kernel](../core.md#kernels) or custom kernel class implementing + - [mlpack kernels](../core.md#kernels) or custom kernel classes implementing a [`Gradient()` function](#advanced-functionality-template-parameters) can be used for the `KernelType` template parameter. - If `kernel` is not specified, a default-constructed `KernelType` will be used. + - A list of usable `KernelType`s supplied with mlpack can be found in the + [advanced functionality section](#advanced-functionality-template-parameters). --- @@ -95,7 +99,7 @@ for (size_t c = 0; c < centroids.n_cols; ++c) |----------|----------|-----------------|-------------| | `radius` | `double` | Radius around each centroid for weighting during centroid recomputation. Larger means higher weights for faraway points. Values less than or equal to 0 mean that the radius will be estimated from data. | `0.0` | | `maxIterations` | `size_t` | Maximum number of iterations of the mean shift algorithm to run. | `1000` | -| `kernel` | [`KernelType`](#advanced-functionality-template-parameters) (default `GaussianKernel`) | Instantiated kernel object to use for density calculations. | `KernelType()` | +| `kernel` | [`KernelType`](#advanced-functionality-template-parameters) | Instantiated kernel object to use for density calculations. | [`GaussianKernel()`](../core.md#gaussiankernel) | ***Notes:*** @@ -137,8 +141,7 @@ for (size_t c = 0; c < centroids.n_cols; ++c) | `centroids` | [`arma::mat`](../matrices.md) | [Column-major](../matrices.md#representing-data-in-mlpack) matrix that centroids will be stored into. | _(N/A)_ | | `assignments` | [`arma::Row`](../matrices.md) | Vector to store cluster assignments for each point into. | _(N/A)_ | | `forceConvergence` | `bool` | If `true`, forces convergence of every cluster, ignoring `maxIterations`. | `false` | -| `useSeeds` | `bool` | If `true`, estimates of high-density regions in the -dataset will be used as initial centroids, instead of the full dataset. | `true` +| `useSeeds` | `bool` | If `true`, estimates of high-density regions in the dataset will be used as initial centroids, instead of the full dataset. | `true` ***Notes***: @@ -196,8 +199,8 @@ std::cout << "MeanShift computed " << centroids.n_cols << " clusters." double sumDist = 0.0; for (size_t i = 0; i < dataset.n_cols; ++i) { - sumDist += mlpack::EuclideanDistance::Evaluate(dataset.col(i), - centroids.col(assignments[i])); + sumDist += mlpack::EuclideanDistance::Evaluate( + dataset.col(i), centroids.col(assignments[i])); } const double avgDist = sumDist / (double) dataset.n_cols; From 9a925ccbdcc4a065dba84a959c15eced5b5473f1 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 18 Jul 2024 15:56:53 -0400 Subject: [PATCH 147/212] Fix link in sidebar. --- doc/sidebar.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/sidebar.html b/doc/sidebar.html index 9d4bfb77c3..a9555f315f 100644 --- a/doc/sidebar.html +++ b/doc/sidebar.html @@ -140,8 +140,8 @@ when the sidebar is built for each page. From 0d0147eb4ad4f5c21e4d22e51f92fdbd5bb2bd77 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 18 Jul 2024 17:32:03 -0400 Subject: [PATCH 148/212] Switch to using both actions from one repository. --- .github/workflows/auto-approve.yml | 2 +- .github/workflows/stickers.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml index 9a8ab15f92..1270e838d1 100644 --- a/.github/workflows/auto-approve.yml +++ b/.github/workflows/auto-approve.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Auto-approve pull requests - uses: rcurtin/auto-approve@v1 + uses: rcurtin/actions/auto-approve@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} approval-message: diff --git a/.github/workflows/stickers.yaml b/.github/workflows/stickers.yaml index b6aa2f5667..aafb7d390b 100644 --- a/.github/workflows/stickers.yaml +++ b/.github/workflows/stickers.yaml @@ -11,7 +11,7 @@ jobs: if: github.event.pull_request.merged == true steps: # Forked version of first-interaction that runs only on first merged PR. - - uses: rcurtin/first-interaction@v1 + - uses: rcurtin/actions/stickers@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} pr-message: "Hello there! Thanks for your contribution. Congratulations on your first contribution to mlpack! If you'd like to add your name to the list of contributors in `COPYRIGHT.txt` and you haven't already, please feel free to push a change to this PR---or, if it gets merged before you can, feel free to open another PR.\n\nIn addition, if you'd like some stickers to put on your laptop, we can get them in the mail for you. Just send an email with your physical mailing address to stickers@mlpack.org, and then one of the mlpack maintainers will put some stickers in an envelope for you. It may take a few weeks to get them, depending on your location. :+1:" From 8ccc35e3d28bb6b1a5eef63792c87caf45a67061 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 19 Jul 2024 15:52:31 +0200 Subject: [PATCH 149/212] Remove arma::fill::zerors from constructors, since we use arma > 10.5 Signed-off-by: Omar Shrit --- .../cf/decomposition_policies/svdplusplus_method.hpp | 4 ++-- .../cf/normalization/item_mean_normalization.hpp | 6 +++--- .../cf/normalization/user_mean_normalization.hpp | 6 +++--- src/mlpack/methods/dbscan/dbscan_impl.hpp | 2 +- .../decision_tree/all_categorical_split_impl.hpp | 8 ++++---- .../best_binary_categorical_split_impl.hpp | 6 +++--- .../methods/decision_tree/decision_tree_impl.hpp | 2 +- .../decision_tree/decision_tree_regressor_impl.hpp | 2 +- src/mlpack/methods/decision_tree/gini_gain.hpp | 2 +- src/mlpack/methods/decision_tree/information_gain.hpp | 2 +- src/mlpack/methods/kde/kde_rules_impl.hpp | 4 ++-- src/mlpack/methods/lars/lars_impl.hpp | 4 ++-- src/mlpack/methods/lmnn/constraints_impl.hpp | 2 +- .../environment/cont_double_pole_cart.hpp | 2 +- .../environment/continuous_mountain_car.hpp | 2 +- .../environment/double_pole_cart.hpp | 2 +- .../environment/mountain_car.hpp | 2 +- .../reinforcement_learning/environment/pendulum.hpp | 2 +- .../worker/n_step_q_learning_worker.hpp | 2 +- .../worker/one_step_q_learning_worker.hpp | 2 +- .../worker/one_step_sarsa_worker.hpp | 2 +- .../methods/svdplusplus/svdplusplus_function_impl.hpp | 10 +++++----- 22 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp b/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp index 28741e1bfc..704d4d1687 100644 --- a/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp +++ b/src/mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp @@ -96,7 +96,7 @@ class SVDPlusPlusPolicy { // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(h.n_rows, arma::fill::zeros); + arma::vec userVec(h.n_rows); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; @@ -124,7 +124,7 @@ class SVDPlusPlusPolicy { // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(h.n_rows, arma::fill::zeros); + arma::vec userVec(h.n_rows); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; diff --git a/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp b/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp index 251574a879..b06f47d49f 100644 --- a/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp +++ b/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp @@ -49,9 +49,9 @@ class ItemMeanNormalization void Normalize(arma::mat& data) { const size_t itemNum = max(data.row(1)) + 1; - itemMean = arma::vec(itemNum, arma::fill::zeros); + itemMean = arma::vec(itemNum); // Number of ratings for each item. - arma::Row ratingNum(itemNum, arma::fill::zeros); + arma::Row ratingNum(itemNum); // Sum ratings for each item. data.each_col([&](arma::vec& datapoint) @@ -89,7 +89,7 @@ class ItemMeanNormalization void Normalize(arma::sp_mat& cleanedData) { // Calculate itemMean. - itemMean = arma::vec(cleanedData.n_rows, arma::fill::zeros); + itemMean = arma::vec(cleanedData.n_rows); arma::Col ratingNum(cleanedData.n_rows, arma::fill::zeros); arma::sp_mat::iterator it = cleanedData.begin(); diff --git a/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp b/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp index 7b991adde5..490a8d0604 100644 --- a/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp +++ b/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp @@ -49,9 +49,9 @@ class UserMeanNormalization void Normalize(arma::mat& data) { const size_t userNum = max(data.row(0)) + 1; - userMean = arma::vec(userNum, arma::fill::zeros); + userMean = arma::vec(userNum); // Number of ratings for each user. - arma::Row ratingNum(userNum, arma::fill::zeros); + arma::Row ratingNum(userNum); // Sum ratings for each user. data.each_col([&](arma::vec& datapoint) @@ -89,7 +89,7 @@ class UserMeanNormalization void Normalize(arma::sp_mat& cleanedData) { // Calculate userMean. - userMean = arma::vec(cleanedData.n_cols, arma::fill::zeros); + userMean = arma::vec(cleanedData.n_cols); arma::Col ratingNum(cleanedData.n_cols, arma::fill::zeros); arma::sp_mat::iterator it = cleanedData.begin(); diff --git a/src/mlpack/methods/dbscan/dbscan_impl.hpp b/src/mlpack/methods/dbscan/dbscan_impl.hpp index e2642de484..f6678bc2a8 100644 --- a/src/mlpack/methods/dbscan/dbscan_impl.hpp +++ b/src/mlpack/methods/dbscan/dbscan_impl.hpp @@ -112,7 +112,7 @@ size_t DBSCAN::Cluster( // Get a count of all clusters. const size_t numClusters = max(assignments) + 1; - arma::Col counts(numClusters, arma::fill::zeros); + arma::Col counts(numClusters); for (size_t i = 0; i < assignments.n_elem; ++i) counts[assignments[i]]++; diff --git a/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp b/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp index 6f0b67ef20..ba77a3461b 100644 --- a/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp +++ b/src/mlpack/methods/decision_tree/all_categorical_split_impl.hpp @@ -32,7 +32,7 @@ double AllCategoricalSplit::SplitIfBetter( { // Count the number of elements in each potential child. const double epsilon = 1e-7; // Tolerance for floating-point errors. - arma::Col counts(numCategories, arma::fill::zeros); + arma::Col counts(numCategories); // If we are using weighted training, split the weights for each child too. arma::vec childWeightSums; @@ -58,7 +58,7 @@ double AllCategoricalSplit::SplitIfBetter( // Calculate the gain of the split. First we have to calculate the labels // that would be assigned to each child. - arma::uvec childPositions(numCategories, arma::fill::zeros); + arma::uvec childPositions(numCategories); std::vector> childLabels(numCategories); std::vector> childWeights(numCategories); @@ -129,7 +129,7 @@ double AllCategoricalSplit::SplitIfBetter( { // Count the number of elements in each potential child. const double epsilon = 1e-7; // Tolerance for floating-point errors. - arma::Col counts(numCategories, arma::fill::zeros); + arma::Col counts(numCategories); // If we are using weighted training, split the weights for each child too. arma::vec childWeightSums; @@ -155,7 +155,7 @@ double AllCategoricalSplit::SplitIfBetter( // Calculate the gain of the split. First we have to calculate the labels // that would be assigned to each child. - arma::uvec childPositions(numCategories, arma::fill::zeros); + arma::uvec childPositions(numCategories); std::vector childResponses(numCategories); std::vector childWeights(numCategories); diff --git a/src/mlpack/methods/decision_tree/best_binary_categorical_split_impl.hpp b/src/mlpack/methods/decision_tree/best_binary_categorical_split_impl.hpp index c5be2c4de2..081d761240 100644 --- a/src/mlpack/methods/decision_tree/best_binary_categorical_split_impl.hpp +++ b/src/mlpack/methods/decision_tree/best_binary_categorical_split_impl.hpp @@ -46,7 +46,7 @@ double BestBinaryCategoricalSplit::SplitIfBetter( { // Order the categories of variable vₖ by their proportion in class C₁ // and map each categorical vₖ to its categorical rank - arma::umat categoryCounts(numCategories, 2, arma::fill::zeros); + arma::umat categoryCounts(numCategories, 2); arma::vec categoryP(numCategories); size_t totalCount; @@ -172,8 +172,8 @@ double BestBinaryCategoricalSplit::SplitIfBetter( // Order the categories of variable vₖ by increasing mean // of the response y. categoryResponse[i, 0] will contain // the mean response for category Cᵢ. - arma::vec categoryResponse(numCategories, arma::fill::zeros); - arma::uvec categoryCounts(numCategories, arma::fill::zeros); + arma::vec categoryResponse(numCategories); + arma::uvec categoryCounts(numCategories); for (size_t i = 0; i < n; ++i) { diff --git a/src/mlpack/methods/decision_tree/decision_tree_impl.hpp b/src/mlpack/methods/decision_tree/decision_tree_impl.hpp index 09913c38d7..8d8776b959 100644 --- a/src/mlpack/methods/decision_tree/decision_tree_impl.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree_impl.hpp @@ -727,7 +727,7 @@ double DecisionTree childCounts(numChildren, arma::fill::zeros); + arma::Row childCounts(numChildren); for (size_t i = begin; i < begin + count; ++i) childCounts[childAssignments[i - begin]]++; diff --git a/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp b/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp index 99e932f892..e6f51c6b1e 100644 --- a/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree_regressor_impl.hpp @@ -708,7 +708,7 @@ double DecisionTreeRegressor childCounts(numChildren, arma::fill::zeros); + arma::Row childCounts(numChildren); for (size_t i = begin; i < begin + count; ++i) childCounts[childAssignments[i - begin]]++; diff --git a/src/mlpack/methods/decision_tree/gini_gain.hpp b/src/mlpack/methods/decision_tree/gini_gain.hpp index ea7d4a5407..35c4d01024 100644 --- a/src/mlpack/methods/decision_tree/gini_gain.hpp +++ b/src/mlpack/methods/decision_tree/gini_gain.hpp @@ -68,7 +68,7 @@ class GiniGain // Count the number of elements in each class. Use four auxiliary vectors // to exploit SIMD instructions if possible. - arma::vec countSpace(4 * numClasses, arma::fill::zeros); + arma::vec countSpace(4 * numClasses); arma::vec counts(countSpace.memptr(), numClasses, false, true); arma::vec counts2(countSpace.memptr() + numClasses, numClasses, false, true); diff --git a/src/mlpack/methods/decision_tree/information_gain.hpp b/src/mlpack/methods/decision_tree/information_gain.hpp index 7cf0f1158e..b1510bff47 100644 --- a/src/mlpack/methods/decision_tree/information_gain.hpp +++ b/src/mlpack/methods/decision_tree/information_gain.hpp @@ -69,7 +69,7 @@ class InformationGain // Count the number of elements in each class. Use four auxiliary vectors // to exploit SIMD instructions if possible. - arma::vec countSpace(4 * numClasses, arma::fill::zeros); + arma::vec countSpace(4 * numClasses); arma::vec counts(countSpace.memptr(), numClasses, false, true); arma::vec counts2(countSpace.memptr() + numClasses, numClasses, false, true); diff --git a/src/mlpack/methods/kde/kde_rules_impl.hpp b/src/mlpack/methods/kde/kde_rules_impl.hpp index 81aeb18658..ec60ab0d37 100644 --- a/src/mlpack/methods/kde/kde_rules_impl.hpp +++ b/src/mlpack/methods/kde/kde_rules_impl.hpp @@ -56,11 +56,11 @@ KDERules::KDERules( scores(0) { // Initialize accumError. - accumError = arma::vec(querySet.n_cols, arma::fill::zeros); + accumError = arma::vec(querySet.n_cols); // Initialize accumMCAlpha only if Monte Carlo estimations are available. if (monteCarlo && kernelIsGaussian) - accumMCAlpha = arma::vec(querySet.n_cols, arma::fill::zeros); + accumMCAlpha = arma::vec(querySet.n_cols); } //! The base case. diff --git a/src/mlpack/methods/lars/lars_impl.hpp b/src/mlpack/methods/lars/lars_impl.hpp index efb6c258d0..2c850a064d 100644 --- a/src/mlpack/methods/lars/lars_impl.hpp +++ b/src/mlpack/methods/lars/lars_impl.hpp @@ -573,8 +573,8 @@ LARS::Train(const MatType& matX, isIgnored.resize(dataRef.n_cols, false); // Initialize yHat and beta. - arma::Col beta(dataRef.n_cols, arma::fill::zeros); - arma::Col yHat(dataRef.n_rows, arma::fill::zeros); + arma::Col beta(dataRef.n_cols); + arma::Col yHat(dataRef.n_rows); arma::Col yHatDirection(dataRef.n_rows, arma::fill::none); diff --git a/src/mlpack/methods/lmnn/constraints_impl.hpp b/src/mlpack/methods/lmnn/constraints_impl.hpp index 27ed28417a..dd5622386d 100644 --- a/src/mlpack/methods/lmnn/constraints_impl.hpp +++ b/src/mlpack/methods/lmnn/constraints_impl.hpp @@ -405,7 +405,7 @@ void Constraints::Triplets( UMatType targetNeighbors(k, dataset.n_cols);; TargetNeighbors(targetNeighbors, dataset, labels, norms); - outputMatrix = UMatType(3, k * k * N , arma::fill::zeros); + outputMatrix = UMatType(3, k * k * N ); #pragma omp parallel for collapse(3) for (size_t i = 0; i < N; ++i) diff --git a/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp b/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp index 7c0120071a..5b2015630c 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/cont_double_pole_cart.hpp @@ -156,7 +156,7 @@ class ContinuousDoublePoleCart // Update the number of steps performed. stepsPerformed++; - arma::vec dydx(6, arma::fill::zeros); + arma::vec dydx(6); dydx[0] = state.Velocity(); dydx[2] = state.AngularVelocity(1); dydx[4] = state.AngularVelocity(2); diff --git a/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp b/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp index f17e60b640..cf10c51f12 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp @@ -37,7 +37,7 @@ class ContinuousMountainCar /** * Construct a state instance. */ - State() : data(dimension, arma::fill::zeros) + State() : data(dimension) { /* Nothing to do here. */ } /** diff --git a/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp b/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp index fc470a56e0..f3040874dc 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/double_pole_cart.hpp @@ -162,7 +162,7 @@ class DoublePoleCart // Update the number of steps performed. stepsPerformed++; - arma::vec dydx(6, arma::fill::zeros); + arma::vec dydx(6); dydx[0] = state.Velocity(); dydx[2] = state.AngularVelocity(1); dydx[4] = state.AngularVelocity(2); diff --git a/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp b/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp index 3698af6d81..d611a5e403 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/mountain_car.hpp @@ -36,7 +36,7 @@ class MountainCar /** * Construct a state instance. */ - State(): data(dimension, arma::fill::zeros) + State(): data(dimension) { /* Nothing to do here. */ } /** diff --git a/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp b/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp index bf44b6ac6d..c77a1fd289 100644 --- a/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp +++ b/src/mlpack/methods/reinforcement_learning/environment/pendulum.hpp @@ -39,7 +39,7 @@ class Pendulum /** * Construct a state instance. */ - State() : theta(0), data(dimension, arma::fill::zeros) + State() : theta(0), data(dimension) { /* Nothing to do here. */ } /** diff --git a/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp b/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp index fa21e97d9e..f534079137 100644 --- a/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp +++ b/src/mlpack/methods/reinforcement_learning/worker/n_step_q_learning_worker.hpp @@ -282,7 +282,7 @@ class NStepQLearningWorker { // Initialize the gradient storage. arma::mat totalGradients(learningNetwork.Parameters().n_rows, - learningNetwork.Parameters().n_cols, arma::fill::zeros); + learningNetwork.Parameters().n_cols); // Bootstrap from the value of next state. arma::colvec actionValue; diff --git a/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp b/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp index 75e1bed513..9d487beb31 100644 --- a/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp +++ b/src/mlpack/methods/reinforcement_learning/worker/one_step_q_learning_worker.hpp @@ -282,7 +282,7 @@ class OneStepQLearningWorker { // Initialize the gradient storage. arma::mat totalGradients(learningNetwork.Parameters().n_rows, - learningNetwork.Parameters().n_cols, arma::fill::zeros); + learningNetwork.Parameters().n_cols); for (size_t i = 0; i < pending.size(); ++i) { TransitionType &transition = pending[i]; diff --git a/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp b/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp index 814b499898..96fb078d8e 100644 --- a/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp +++ b/src/mlpack/methods/reinforcement_learning/worker/one_step_sarsa_worker.hpp @@ -295,7 +295,7 @@ class OneStepSarsaWorker { // Initialize the gradient storage. arma::mat totalGradients(learningNetwork.Parameters().n_rows, - learningNetwork.Parameters().n_cols, arma::fill::zeros); + learningNetwork.Parameters().n_cols); for (size_t i = 0; i < pending.size(); ++i) { TransitionType &transition = pending[i]; diff --git a/src/mlpack/methods/svdplusplus/svdplusplus_function_impl.hpp b/src/mlpack/methods/svdplusplus/svdplusplus_function_impl.hpp index 2b6eaa3e0e..b829651a89 100644 --- a/src/mlpack/methods/svdplusplus/svdplusplus_function_impl.hpp +++ b/src/mlpack/methods/svdplusplus/svdplusplus_function_impl.hpp @@ -96,7 +96,7 @@ double SVDPlusPlusFunction::Evaluate(const arma::mat& parameters, // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(rank, arma::fill::zeros); + arma::vec userVec(rank); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; @@ -169,7 +169,7 @@ void SVDPlusPlusFunction::Gradient(const arma::mat& parameters, // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(rank, arma::fill::zeros); + arma::vec userVec(rank); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; @@ -236,7 +236,7 @@ void SVDPlusPlusFunction::Gradient(const arma::mat& parameters, // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(rank, arma::fill::zeros); + arma::vec userVec(rank); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; @@ -343,7 +343,7 @@ double StandardSGD::Optimize( // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(rank, arma::fill::zeros); + arma::vec userVec(rank); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; @@ -479,7 +479,7 @@ inline double ParallelSGD::Optimize( const double itemBias = iterate(rank, item); // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(rank, arma::fill::zeros); + arma::vec userVec(rank); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; From 9fed96d649853629f77bed06c3c79ba0665af373 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 19 Jul 2024 15:56:39 +0200 Subject: [PATCH 150/212] Do the same for the ANN code base Signed-off-by: Omar Shrit --- src/mlpack/methods/ann/layer/convolution_impl.hpp | 2 +- src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp | 2 +- src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/ann/layer/convolution_impl.hpp b/src/mlpack/methods/ann/layer/convolution_impl.hpp index e2b8f9ee83..1351cd3339 100644 --- a/src/mlpack/methods/ann/layer/convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/convolution_impl.hpp @@ -422,7 +422,7 @@ void ConvolutionType< } MatType output(apparentWidth * apparentHeight * inMaps * higherInDimensions, - batchSize, GetFillType::zeros); + batchSize); CubeType outputCube; MakeAlias(outputCube, output, apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); diff --git a/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp b/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp index aa6e9af354..a9069751b9 100644 --- a/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/grouped_convolution_impl.hpp @@ -440,7 +440,7 @@ void GroupedConvolutionType< } MatType output(apparentWidth * apparentHeight * inMaps * higherInDimensions, - batchSize, GetFillType::zeros); + batchSize); CubeType outputCube; MakeAlias(outputCube, output, apparentWidth, apparentHeight, inMaps * higherInDimensions * batchSize); diff --git a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp index fe79182a88..d1dbad52ab 100644 --- a/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp +++ b/src/mlpack/methods/ann/loss_functions/hinge_loss_impl.hpp @@ -31,7 +31,7 @@ typename MatType::elem_type HingeLossType::Forward( const MatType& target) { MatType temp = target - (target == 0); - MatType tempZeros(size(target), GetFillType::zeros); + MatType tempZeros(size(target)); MatType loss = max(tempZeros, 1 - prediction % temp); From bb89c6721f891e0a0a033018318d385184781cd4 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 19 Jul 2024 15:58:23 +0200 Subject: [PATCH 151/212] Do the same thing in core/ Signed-off-by: Omar Shrit --- .../core/distributions/gamma_distribution_impl.hpp | 6 +++--- .../rectangle_tree/discrete_hilbert_value_impl.hpp | 2 +- .../tree/rectangle_tree/r_star_tree_split_impl.hpp | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/mlpack/core/distributions/gamma_distribution_impl.hpp b/src/mlpack/core/distributions/gamma_distribution_impl.hpp index 87c02d068e..875a437013 100644 --- a/src/mlpack/core/distributions/gamma_distribution_impl.hpp +++ b/src/mlpack/core/distributions/gamma_distribution_impl.hpp @@ -74,9 +74,9 @@ inline void GammaDistribution::Train(const arma::mat& rdata, if (arma::size(rdata) == arma::size(arma::mat())) return; - arma::vec meanLogxVec(rdata.n_rows, arma::fill::zeros); - arma::vec meanxVec(rdata.n_rows, arma::fill::zeros); - arma::vec logMeanxVec(rdata.n_rows, arma::fill::zeros); + arma::vec meanLogxVec(rdata.n_rows); + arma::vec meanxVec(rdata.n_rows); + arma::vec logMeanxVec(rdata.n_rows); for (size_t i = 0; i < rdata.n_cols; ++i) { diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp index afaa845123..78b0576392 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -243,7 +243,7 @@ CalculateValue(const VecType& pt, res(i) ^= t; // We should rearrange bits in order to compare two Hilbert values faster. - arma::Col rearrangedResult(pt.n_rows, arma::fill::zeros); + arma::Col rearrangedResult(pt.n_rows); for (size_t i = 0; i < order; ++i) for (size_t j = 0; j < pt.n_rows; ++j) 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 6ada7dee23..8c9d43624e 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 @@ -106,9 +106,9 @@ void RStarTreeSplit::PickLeafSplit(TreeType* tree, // We'll store each of the three scores for each distribution. const size_t numPossibleSplits = tree->MaxLeafSize() - 2 * tree->MinLeafSize() + 2; - arma::Col areas(numPossibleSplits, arma::fill::zeros); - arma::Col margins(numPossibleSplits, arma::fill::zeros); - arma::Col overlaps(numPossibleSplits, arma::fill::zeros); + arma::Col areas(numPossibleSplits); + arma::Col margins(numPossibleSplits); + arma::Col overlaps(numPossibleSplits); for (size_t i = 0; i < numPossibleSplits; ++i) { @@ -310,9 +310,9 @@ bool RStarTreeSplit::SplitNonLeafNode( // each rectangle. const size_t numPossibleSplits = tree->MaxNumChildren() - 2 * tree->MinNumChildren() + 2; - arma::Col areas(2 * numPossibleSplits, arma::fill::zeros); - arma::Col margins(2 * numPossibleSplits, arma::fill::zeros); - arma::Col overlaps(2 * numPossibleSplits, arma::fill::zeros); + arma::Col areas(2 * numPossibleSplits); + arma::Col margins(2 * numPossibleSplits); + arma::Col overlaps(2 * numPossibleSplits); for (size_t i = 0; i < numPossibleSplits; ++i) { From 5dd9f00673161ef01c0bccc553f4ddb545f5520b Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 19 Jul 2024 16:04:37 +0200 Subject: [PATCH 152/212] Do the same in the entire tests repository Signed-off-by: Omar Shrit --- src/mlpack/tests/ann/async_learning_test.cpp | 6 +- .../tests/ann/convolutional_network_test.cpp | 2 +- src/mlpack/tests/ann/layer/convolution.cpp | 2 +- .../tests/ann/layer/grouped_convolution.cpp | 2 +- .../tests/ann/not_adapted/ann_layer_test.cpp | 34 +++--- .../ann/not_adapted/rbm_network_test.cpp | 2 +- .../tests/ann/recurrent_network_test.cpp | 4 +- src/mlpack/tests/cv_test.cpp | 2 +- src/mlpack/tests/kde_test.cpp | 100 +++++++++--------- src/mlpack/tests/lmnn_test.cpp | 4 +- src/mlpack/tests/logistic_regression_test.cpp | 6 +- src/mlpack/tests/math_test.cpp | 40 +++---- src/mlpack/tests/scaling_test.cpp | 2 +- src/mlpack/tests/softmax_regression_test.cpp | 2 +- src/mlpack/tests/svdplusplus_test.cpp | 10 +- 15 files changed, 109 insertions(+), 109 deletions(-) diff --git a/src/mlpack/tests/ann/async_learning_test.cpp b/src/mlpack/tests/ann/async_learning_test.cpp index cca1e52128..eab6351264 100644 --- a/src/mlpack/tests/ann/async_learning_test.cpp +++ b/src/mlpack/tests/ann/async_learning_test.cpp @@ -59,7 +59,7 @@ TEST_CASE("OneStepQLearningTest", "[AsyncLearningTest]") CartPole, decltype(model), ens::VanillaUpdate, decltype(policy)> agent(std::move(config), std::move(model), std::move(policy)); - arma::vec rewards(20, arma::fill::zeros); + arma::vec rewards(20); size_t pos = 0; size_t testEpisodes = 0; auto measure = [&rewards, &pos, &testEpisodes](double reward) @@ -137,7 +137,7 @@ TEST_CASE("OneStepSarsaTest", "[AsyncLearningTest]") decltype(policy)> agent(std::move(config), std::move(model), std::move(policy)); - arma::vec rewards(20, arma::fill::zeros); + arma::vec rewards(20); size_t pos = 0; size_t testEpisodes = 0; auto measure = [&rewards, &pos, &testEpisodes](double reward) @@ -210,7 +210,7 @@ TEST_CASE("NStepQLearningTest", "[AsyncLearningTest]") CartPole, decltype(model), ens::VanillaUpdate, decltype(policy)> agent(std::move(config), std::move(model), std::move(policy)); - arma::vec rewards(20, arma::fill::zeros); + arma::vec rewards(20); size_t pos = 0; size_t testEpisodes = 0; auto measure = [&rewards, &pos, &testEpisodes](double reward) diff --git a/src/mlpack/tests/ann/convolutional_network_test.cpp b/src/mlpack/tests/ann/convolutional_network_test.cpp index 209ac5b131..06ee7cdc1e 100644 --- a/src/mlpack/tests/ann/convolutional_network_test.cpp +++ b/src/mlpack/tests/ann/convolutional_network_test.cpp @@ -313,7 +313,7 @@ TEST_CASE("VanillaNetworkBatchSizeTest", "[ConvolutionalNetworkTest]") // Now compute results with a batch size of 1. arma::mat singleResults(results.n_rows, results.n_cols); - arma::mat singleGradient(gradient.n_rows, gradient.n_cols, arma::fill::zeros); + arma::mat singleGradient(gradient.n_rows, gradient.n_cols); double singleObj = 0.0; for (size_t i = 0; i < batchSize; ++i) diff --git a/src/mlpack/tests/ann/layer/convolution.cpp b/src/mlpack/tests/ann/layer/convolution.cpp index 4b932bdfe1..6097f88d3d 100644 --- a/src/mlpack/tests/ann/layer/convolution.cpp +++ b/src/mlpack/tests/ann/layer/convolution.cpp @@ -454,7 +454,7 @@ TEST_CASE("NonSquareConvolutionTest", "[ANNLayerTest]") module1.SetWeights(weights1); arma::mat data(49, 10, arma::fill::randu); - arma::mat forwardResult(module1.OutputSize(), 10, arma::fill::zeros); + arma::mat forwardResult(module1.OutputSize(), 10); REQUIRE_NOTHROW(module1.Forward(data, forwardResult)); arma::mat backwardResult(49, 10); REQUIRE_NOTHROW(module1.Backward(data, forwardResult, forwardResult, backwardResult)); diff --git a/src/mlpack/tests/ann/layer/grouped_convolution.cpp b/src/mlpack/tests/ann/layer/grouped_convolution.cpp index d203e669e6..b64565dcbf 100644 --- a/src/mlpack/tests/ann/layer/grouped_convolution.cpp +++ b/src/mlpack/tests/ann/layer/grouped_convolution.cpp @@ -215,7 +215,7 @@ TEST_CASE("NonSquareGroupedConvolutionTest", "[ANNLayerTest]") module1.SetWeights(weights1); arma::mat data(49, 10, arma::fill::randu); - arma::mat forwardResult(module1.OutputSize(), 10, arma::fill::zeros); + arma::mat forwardResult(module1.OutputSize(), 10); REQUIRE_NOTHROW(module1.Forward(data, forwardResult)); arma::mat backwardResult(49, 10); REQUIRE_NOTHROW(module1.Backward(data, forwardResult, forwardResult, backwardResult)); diff --git a/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp b/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp index fd3a81ee00..f2a813b632 100644 --- a/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp +++ b/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp @@ -2155,7 +2155,7 @@ TEST_CASE("SimpleTransposedConvolutionLayerTest", "[ANNLayerTest]") TransposedConvolution module1(1, 1, 3, 3, 1, 1, 0, 0, 4, 4, 6, 6); // Test the forward function. input = arma::linspace(0, 15, 16); - module1.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); + module1.Parameters() = arma::mat(9 + 1, 1); module1.Parameters()(0) = 1.0; module1.Parameters()(8) = 2.0; module1.Reset(); @@ -2171,7 +2171,7 @@ TEST_CASE("SimpleTransposedConvolutionLayerTest", "[ANNLayerTest]") TransposedConvolution module2(1, 1, 4, 4, 1, 1, 1, 1, 5, 5, 6, 6); // Test the forward function. input = arma::linspace(0, 24, 25); - module2.Parameters() = arma::mat(16 + 1, 1, arma::fill::zeros); + module2.Parameters() = arma::mat(16 + 1, 1); module2.Parameters()(0) = 1.0; module2.Parameters()(3) = 1.0; module2.Parameters()(6) = 1.0; @@ -2191,7 +2191,7 @@ TEST_CASE("SimpleTransposedConvolutionLayerTest", "[ANNLayerTest]") TransposedConvolution module3(1, 1, 3, 3, 1, 1, 1, 1, 5, 5, 5, 5); // Test the forward function. input = arma::linspace(0, 24, 25); - module3.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); + module3.Parameters() = arma::mat(9 + 1, 1); module3.Parameters()(1) = 2.0; module3.Parameters()(2) = 4.0; module3.Parameters()(3) = 3.0; @@ -2209,7 +2209,7 @@ TEST_CASE("SimpleTransposedConvolutionLayerTest", "[ANNLayerTest]") TransposedConvolution module4(1, 1, 3, 3, 1, 1, 0, 0, 5, 5, 7, 7); // Test the forward function. input = arma::linspace(0, 24, 25); - module4.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); + module4.Parameters() = arma::mat(9 + 1, 1); module4.Parameters()(2) = 2.0; module4.Parameters()(4) = 4.0; module4.Parameters()(6) = 6.0; @@ -2227,7 +2227,7 @@ TEST_CASE("SimpleTransposedConvolutionLayerTest", "[ANNLayerTest]") TransposedConvolution module5(1, 1, 3, 3, 2, 2, 0, 0, 2, 2, 5, 5); // Test the forward function. input = arma::linspace(0, 3, 4); - module5.Parameters() = arma::mat(25 + 1, 1, arma::fill::zeros); + module5.Parameters() = arma::mat(25 + 1, 1); module5.Parameters()(2) = 8.0; module5.Parameters()(4) = 6.0; module5.Parameters()(6) = 4.0; @@ -2245,7 +2245,7 @@ TEST_CASE("SimpleTransposedConvolutionLayerTest", "[ANNLayerTest]") TransposedConvolution module6(1, 1, 3, 3, 2, 2, 1, 1, 3, 3, 5, 5); // Test the forward function. input = arma::linspace(0, 8, 9); - module6.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); + module6.Parameters() = arma::mat(9 + 1, 1); module6.Parameters()(0) = 8.0; module6.Parameters()(3) = 6.0; module6.Parameters()(6) = 2.0; @@ -2263,7 +2263,7 @@ TEST_CASE("SimpleTransposedConvolutionLayerTest", "[ANNLayerTest]") TransposedConvolution module7(1, 1, 3, 3, 2, 2, 1, 1, 3, 3, 6, 6); // Test the forward function. input = arma::linspace(0, 8, 9); - module7.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); + module7.Parameters() = arma::mat(9 + 1, 1); module7.Parameters()(0) = 8.0; module7.Parameters()(2) = 6.0; module7.Parameters()(4) = 2.0; @@ -2420,7 +2420,7 @@ TEST_CASE("SimpleMultiplyMergeLayerTest", "[ANNLayerTest]") // AtrousConvolution<> module1(1, 1, 3, 3, 1, 1, 0, 0, 7, 7, 2, 2); // // Test the Forward function. // input = arma::linspace(0, 48, 49); -// module1.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); +// module1.Parameters() = arma::mat(9 + 1, 1); // module1.Parameters()(0) = 1.0; // module1.Parameters()(8) = 2.0; // module1.Reset(); @@ -2435,7 +2435,7 @@ TEST_CASE("SimpleMultiplyMergeLayerTest", "[ANNLayerTest]") // AtrousConvolution<> module2(1, 1, 3, 3, 2, 2, 0, 0, 7, 7, 2, 2); // // Test the forward function. // input = arma::linspace(0, 48, 49); -// module2.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); +// module2.Parameters() = arma::mat(9 + 1, 1); // module2.Parameters()(0) = 1.0; // module2.Parameters()(3) = 1.0; // module2.Parameters()(6) = 1.0; @@ -2565,7 +2565,7 @@ TEST_CASE("SimpleMultiplyMergeLayerTest", "[ANNLayerTest]") // // Test the Forward function. // input = arma::linspace(0, 48, 49); -// module1.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); +// module1.Parameters() = arma::mat(9 + 1, 1); // module1.Reset(); // module1.Forward(input, output); @@ -2583,7 +2583,7 @@ TEST_CASE("SimpleMultiplyMergeLayerTest", "[ANNLayerTest]") // // Test the forward function. // input = arma::linspace(0, 48, 49); -// module2.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); +// module2.Parameters() = arma::mat(9 + 1, 1); // module2.Reset(); // module2.Forward(input, output); @@ -3337,7 +3337,7 @@ TEST_CASE("TransposedConvolutionLayerPaddingTest", "[ANNLayerTest]") // Test the forward function. // Valid Should give the same result. input = arma::linspace(0, 15, 16); - module1.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); + module1.Parameters() = arma::mat(9 + 1, 1); module1.Reset(); module1.Forward(input, output); // Value calculated using tensorflow.nn.conv2d_transpose(). @@ -3353,7 +3353,7 @@ TEST_CASE("TransposedConvolutionLayerPaddingTest", "[ANNLayerTest]") 2, 2, 5, 5, "VALID"); // Test the forward function. input = arma::linspace(0, 3, 4); - module2.Parameters() = arma::mat(25 + 1, 1, arma::fill::zeros); + module2.Parameters() = arma::mat(25 + 1, 1); module2.Parameters()(2) = 8.0; module2.Parameters()(4) = 6.0; module2.Parameters()(6) = 4.0; @@ -3371,7 +3371,7 @@ TEST_CASE("TransposedConvolutionLayerPaddingTest", "[ANNLayerTest]") TransposedConvolution module3(1, 1, 3, 3, 2, 2, 0, 0, 3, 3, 3, 3, "SAME"); // Test the forward function. input = arma::linspace(0, 8, 9); - module3.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); + module3.Parameters() = arma::mat(9 + 1, 1); module3.Reset(); module3.Forward(input, output); REQUIRE(accu(output) == 0); @@ -3388,7 +3388,7 @@ TEST_CASE("TransposedConvolutionLayerPaddingTest", "[ANNLayerTest]") 5, 5, 5, 5, "SAME"); // Test the forward function. input = arma::linspace(0, 24, 25); - module4.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros); + module4.Parameters() = arma::mat(9 + 1, 1); module4.Reset(); module4.Forward(input, output); REQUIRE(accu(output) == 0); @@ -3402,7 +3402,7 @@ TEST_CASE("TransposedConvolutionLayerPaddingTest", "[ANNLayerTest]") TransposedConvolution module5(1, 1, 3, 3, 2, 2, 0, 0, 2, 2, 2, 2, "SAME"); // Test the forward function. input = arma::linspace(0, 3, 4); - module5.Parameters() = arma::mat(25 + 1, 1, arma::fill::zeros); + module5.Parameters() = arma::mat(25 + 1, 1); module5.Reset(); module5.Forward(input, output); REQUIRE(accu(output) == 0); @@ -3416,7 +3416,7 @@ TEST_CASE("TransposedConvolutionLayerPaddingTest", "[ANNLayerTest]") TransposedConvolution module6(1, 1, 4, 4, 1, 1, 1, 1, 5, 5, 5, 5, "SAME"); // Test the forward function. input = arma::linspace(0, 24, 25); - module6.Parameters() = arma::mat(16 + 1, 1, arma::fill::zeros); + module6.Parameters() = arma::mat(16 + 1, 1); module6.Reset(); module6.Forward(input, output); REQUIRE(accu(output) == 0); diff --git a/src/mlpack/tests/ann/not_adapted/rbm_network_test.cpp b/src/mlpack/tests/ann/not_adapted/rbm_network_test.cpp index feacd4339a..a876a2aeac 100644 --- a/src/mlpack/tests/ann/not_adapted/rbm_network_test.cpp +++ b/src/mlpack/tests/ann/not_adapted/rbm_network_test.cpp @@ -227,7 +227,7 @@ void BuildVanillaNetwork(MatType& trainData, // Check free energy. arma::Mat freeEnergy = MatType( "-0.87523715, 0.50615066, 0.46923476, 1.21509084;"); - arma::vec calculatedFreeEnergy(4, arma::fill::zeros); + arma::vec calculatedFreeEnergy(4); for (size_t i = 0; i < trainData.n_cols; ++i) { calculatedFreeEnergy(i) = model.FreeEnergy(trainData.col(i)); diff --git a/src/mlpack/tests/ann/recurrent_network_test.cpp b/src/mlpack/tests/ann/recurrent_network_test.cpp index 5dc3f5676a..fc150ab516 100644 --- a/src/mlpack/tests/ann/recurrent_network_test.cpp +++ b/src/mlpack/tests/ann/recurrent_network_test.cpp @@ -362,7 +362,7 @@ TEST_CASE("LargeRhoValueRnnTest", "[RecurrentNetworkTest]") // Rows: number of dimensions. // Cols: number of sequences/points. // Slices: number of steps in sequences. - MatType result(numLetters, 1, strLen, arma::fill::zeros); + MatType result(numLetters, 1, strLen); for (size_t i = 0; i < strLen; ++i) { result.at(static_cast(line[i]), 0, i) = 1.0; @@ -375,7 +375,7 @@ TEST_CASE("LargeRhoValueRnnTest", "[RecurrentNetworkTest]") const auto strLen = strlen(line); // Responses for NegativeLogLikelihood should be // non-one-hot-encoded class IDs (from 0 to num_classes - 1). - MatType result(1, 1, strLen, arma::fill::zeros); + MatType result(1, 1, strLen); // The response is the *next* letter in the sequence. for (size_t i = 0; i < strLen - 1; ++i) { diff --git a/src/mlpack/tests/cv_test.cpp b/src/mlpack/tests/cv_test.cpp index 8ee7ec8e48..b78e651f9d 100644 --- a/src/mlpack/tests/cv_test.cpp +++ b/src/mlpack/tests/cv_test.cpp @@ -460,7 +460,7 @@ TEST_CASE("FilterNANCVTest", "[CVTest]") // Create a dataset with only one positive label, so it will not be in every // fold. arma::mat data(3, 10, arma::fill::randu); - arma::Row labels(10, arma::fill::zeros); + arma::Row labels(10); labels[0] = 1; const size_t numClasses = 2; diff --git a/src/mlpack/tests/kde_test.cpp b/src/mlpack/tests/kde_test.cpp index 1f79bb4a38..69aaf11b6b 100644 --- a/src/mlpack/tests/kde_test.cpp +++ b/src/mlpack/tests/kde_test.cpp @@ -88,8 +88,8 @@ TEST_CASE("KDETreeAsArguments", "[KDETest]") {-2.1, 1.0} }; arma::inplace_trans(reference); arma::inplace_trans(query); - arma::vec estimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec estimationsResult = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec estimations = arma::vec(query.n_cols); + arma::vec estimationsResult = arma::vec(query.n_cols); const double kernelBandwidth = 0.8; // Get brute force results. @@ -124,8 +124,8 @@ TEST_CASE("GaussianKDEBruteForceTest", "[KDETest]") { arma::mat reference = arma::randu(2, 200); arma::mat query = arma::randu(2, 60); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.12; const double relError = 0.05; @@ -155,8 +155,8 @@ TEST_CASE("GaussianSingleKDEBruteForceTest", "[KDETest]") { arma::mat reference = arma::randu(2, 300); arma::mat query = arma::randu(2, 100); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.3; const double relError = 0.04; @@ -187,8 +187,8 @@ TEST_CASE("EpanechnikovCoverSingleKDETest", "[KDETest]") { arma::mat reference = arma::randu(2, 300); arma::mat query = arma::randu(2, 100); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 1.1; const double relError = 0.08; @@ -219,8 +219,8 @@ TEST_CASE("GaussianCoverSingleKDETest", "[KDETest]") { arma::mat reference = arma::randu(2, 300); arma::mat query = arma::randu(2, 100); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 1.1; const double relError = 0.08; @@ -251,8 +251,8 @@ TEST_CASE("EpanechnikovOctreeSingleKDETest", "[KDETest]") { arma::mat reference = arma::randu(2, 300); arma::mat query = arma::randu(2, 100); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 1.0; const double relError = 0.05; @@ -282,8 +282,8 @@ TEST_CASE("BallTreeGaussianKDETest", "[KDETest]") { arma::mat reference = arma::randu(2, 200); arma::mat query = arma::randu(2, 60); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.4; const double relError = 0.05; @@ -322,8 +322,8 @@ TEST_CASE("OctreeGaussianKDETest", "[KDETest]") { arma::mat reference = arma::randu(2, 500); arma::mat query = arma::randu(2, 200); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.3; const double relError = 0.01; @@ -353,8 +353,8 @@ TEST_CASE("RTreeGaussianKDETest", "[KDETest]") { arma::mat reference = arma::randu(2, 500); arma::mat query = arma::randu(2, 200); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.3; const double relError = 0.01; @@ -385,8 +385,8 @@ TEST_CASE("StandardCoverTreeGaussianKDETest", "[KDETest]") { arma::mat reference = arma::randu(2, 500); arma::mat query = arma::randu(2, 200); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.3; const double relError = 0.01; @@ -417,8 +417,8 @@ TEST_CASE("StandardCoverTreeEpanechnikovKDETest", "[KDETest]") { arma::mat reference = arma::randu(2, 500); arma::mat query = arma::randu(2, 200); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.3; const double relError = 0.01; @@ -451,8 +451,8 @@ TEST_CASE("DuplicatedReferenceSampleKDETest", "[KDETest]") { arma::mat reference = arma::randu(2, 30); arma::mat query = arma::randu(2, 10); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.4; const double relError = 0.05; @@ -494,7 +494,7 @@ TEST_CASE("DuplicatedQuerySampleKDETest", "[KDETest]") { arma::mat reference = arma::randu(2, 30); arma::mat query = arma::randu(2, 10); - arma::vec estimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec estimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.4; const double relError = 0.05; @@ -529,8 +529,8 @@ TEST_CASE("BreadthFirstKDETest", "[KDETest]") { arma::mat reference = arma::randu(2, 200); arma::mat query = arma::randu(2, 60); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.8; const double relError = 0.01; @@ -565,8 +565,8 @@ TEST_CASE("OneDimensionalTest", "[KDETest]") { arma::mat reference = arma::randu(1, 200); arma::mat query = arma::randu(1, 60); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.7; const double relError = 0.01; @@ -596,7 +596,7 @@ TEST_CASE("EmptyReferenceTest", "[KDETest]") { arma::mat reference; arma::mat query = arma::randu(1, 10); - arma::vec estimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec estimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.7; const double relError = 0.01; @@ -626,7 +626,7 @@ TEST_CASE("EvaluationMatchDimensionsTest", "[KDETest]") { arma::mat reference = arma::randu(3, 10); arma::mat query = arma::randu(1, 10); - arma::vec estimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec estimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.7; const double relError = 0.01; @@ -660,7 +660,7 @@ TEST_CASE("EmptyQuerySetTest", "[KDETest]") arma::mat reference = arma::randu(1, 10); arma::mat query; // Set estimations to the wrong size. - arma::vec estimations(33, arma::fill::zeros); + arma::vec estimations(33); const double kernelBandwidth = 0.7; const double relError = 0.01; @@ -719,7 +719,7 @@ TEST_CASE("KDESerializationTest", "[KDETest]") // Get estimations to compare. arma::mat query = arma::randu(4, 100);; - arma::vec estimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec estimations = arma::vec(query.n_cols); kde.Evaluate(query, estimations); // Initialize serialized objects. @@ -775,9 +775,9 @@ TEST_CASE("KDESerializationTest", "[KDETest]") REQUIRE(kdeBinary.MCBreakCoef() == Approx(breakCoef).epsilon(1e-10)); // Test if execution gives the same result. - arma::vec xmlEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec textEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec binEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec xmlEstimations = arma::vec(query.n_cols); + arma::vec textEstimations = arma::vec(query.n_cols); + arma::vec binEstimations = arma::vec(query.n_cols); kdeXml.Evaluate(query, xmlEstimations); kdeText.Evaluate(query, textEstimations); @@ -883,8 +883,8 @@ TEST_CASE("GaussianSingleKDTreeMonteCarloKDE", "[KDETest]") { arma::mat reference = arma::randu(2, 3000); arma::mat query = arma::randu(2, 100); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.35; const double relError = 0.05; @@ -933,8 +933,8 @@ TEST_CASE("GaussianSingleCoverTreeMonteCarloKDE", "[KDETest]") { arma::mat reference = arma::randu(2, 3000); arma::mat query = arma::randu(2, 100); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.35; const double relError = 0.05; @@ -983,8 +983,8 @@ TEST_CASE("GaussianSingleOctreeMonteCarloKDE", "[KDETest]") { arma::mat reference = arma::randu(2, 3000); arma::mat query = arma::randu(2, 100); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.55; const double relError = 0.02; @@ -1033,8 +1033,8 @@ TEST_CASE("GaussianDualKDTreeMonteCarloKDE", "[KDETest]") { arma::mat reference = arma::randu(2, 3000); arma::mat query = arma::randu(2, 200); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.4; const double relError = 0.05; @@ -1083,8 +1083,8 @@ TEST_CASE("GaussianDualCoverTreeMonteCarloKDE", "[KDETest]") { arma::mat reference = arma::randu(2, 3000); arma::mat query = arma::randu(2, 200); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.5; const double relError = 0.025; @@ -1133,8 +1133,8 @@ TEST_CASE("GaussianDualOctreeMonteCarloKDE", "[KDETest]") { arma::mat reference = arma::randu(2, 3000); arma::mat query = arma::randu(2, 200); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.7; const double relError = 0.03; @@ -1183,8 +1183,8 @@ TEST_CASE("GaussianBreadthDualKDTreeMonteCarloKDE", "[KDETest]") { arma::mat reference = arma::randu(2, 3000); arma::mat query = arma::randu(2, 200); - arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros); - arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros); + arma::vec bfEstimations = arma::vec(query.n_cols); + arma::vec treeEstimations = arma::vec(query.n_cols); const double kernelBandwidth = 0.7; const double relError = 0.025; diff --git a/src/mlpack/tests/lmnn_test.cpp b/src/mlpack/tests/lmnn_test.cpp index 6b0c0d254a..394474511b 100644 --- a/src/mlpack/tests/lmnn_test.cpp +++ b/src/mlpack/tests/lmnn_test.cpp @@ -50,7 +50,7 @@ TEMPLATE_TEST_CASE("LMNNTargetNeighborsTest", "[LMNNTest]", float, double) } //! Store target neighbors of data points. - arma::umat targetNeighbors(1, dataset.n_cols, arma::fill::zeros); + arma::umat targetNeighbors(1, dataset.n_cols); constraint.TargetNeighbors(targetNeighbors, dataset, labels, norm); @@ -85,7 +85,7 @@ TEMPLATE_TEST_CASE("LMNNImpostorsTest", "[LMNNTest]", float, double) } //! Store impostors of data points. - arma::umat impostors(1, dataset.n_cols, arma::fill::zeros); + arma::umat impostors(1, dataset.n_cols); constraint.Impostors(impostors, dataset, labels, norm); diff --git a/src/mlpack/tests/logistic_regression_test.cpp b/src/mlpack/tests/logistic_regression_test.cpp index c7bc23556a..35c93775e6 100644 --- a/src/mlpack/tests/logistic_regression_test.cpp +++ b/src/mlpack/tests/logistic_regression_test.cpp @@ -1099,7 +1099,7 @@ TEMPLATE_TEST_CASE("LogisticRegressionAllConstructorsTest", // Create random data. MatType data(50, 1000, arma::fill::randu); - arma::Row labels(1000, arma::fill::zeros); + arma::Row labels(1000); labels.subvec(500, 999).fill(1); // Empty constructor. @@ -1162,7 +1162,7 @@ TEMPLATE_TEST_CASE("LogisticRegressionAllTrainTest", "[LogisticRegressionTest]", // Create random data. MatType data(50, 1000, arma::fill::randu); - arma::Row labels(1000, arma::fill::zeros); + arma::Row labels(1000); labels.subvec(500, 999).fill(1); // Construct all objects that we will use, but don't train. @@ -1236,7 +1236,7 @@ TEST_CASE("LogisticRegressionResetTest", "[LogisticRegressionTest]") { // Create random data. arma::mat data(50, 1000, arma::fill::randu); - arma::Row labels(1000, arma::fill::zeros); + arma::Row labels(1000); labels.subvec(500, 999).fill(1); // Create two logistic regression models. diff --git a/src/mlpack/tests/math_test.cpp b/src/mlpack/tests/math_test.cpp index f6f89b011f..fa1815a7dc 100644 --- a/src/mlpack/tests/math_test.cpp +++ b/src/mlpack/tests/math_test.cpp @@ -583,7 +583,7 @@ TEST_CASE("RangeContainsRange", "[MathTest]") */ TEST_CASE("ShuffleTest", "[MathTest]") { - arma::mat data(3, 10, arma::fill::zeros); + arma::mat data(3, 10); arma::Row labels(10); for (size_t i = 0; i < 10; ++i) { @@ -601,7 +601,7 @@ TEST_CASE("ShuffleTest", "[MathTest]") REQUIRE(outputLabels.n_elem == labels.n_elem); // Make sure we only have each point once. - arma::Row counts(10, arma::fill::zeros); + arma::Row counts(10); for (size_t i = 0; i < 10; ++i) { REQUIRE((size_t) outputData(0, i) == outputLabels[i]); @@ -639,7 +639,7 @@ TEST_CASE("SparseShuffleTest", "[MathTest]") REQUIRE(outputLabels.n_elem == labels.n_elem); // Make sure we only have each point once. - arma::Row counts(10, arma::fill::zeros); + arma::Row counts(10); for (size_t i = 0; i < 10; ++i) { REQUIRE((size_t) outputData(0, i) == outputLabels[i]); @@ -657,7 +657,7 @@ TEST_CASE("SparseShuffleTest", "[MathTest]") */ TEST_CASE("CubeShuffleTest", "[MathTest]") { - arma::cube data(3, 10, 5, arma::fill::zeros); + arma::cube data(3, 10, 5); arma::cube labels(1, 10, 5); for (size_t i = 0; i < labels.n_slices; ++i) { @@ -681,7 +681,7 @@ TEST_CASE("CubeShuffleTest", "[MathTest]") REQUIRE(outputLabels.n_slices == labels.n_slices); // Make sure we only have each point once. - arma::Row counts(10, arma::fill::zeros); + arma::Row counts(10); for (size_t i = 0; i < 10; ++i) { for (size_t s = 0; s < data.n_slices; ++s) @@ -701,7 +701,7 @@ TEST_CASE("CubeShuffleTest", "[MathTest]") */ TEST_CASE("ShuffleWeightsTest", "[MathTest]") { - arma::mat data(3, 10, arma::fill::zeros); + arma::mat data(3, 10); arma::Row labels(10); arma::rowvec weights(10); for (size_t i = 0; i < 10; ++i) @@ -723,8 +723,8 @@ TEST_CASE("ShuffleWeightsTest", "[MathTest]") REQUIRE(outputWeights.n_elem == weights.n_elem); // Make sure we only have each point once. - arma::Row counts(10, arma::fill::zeros); - arma::Row weightCounts(10, arma::fill::zeros); + arma::Row counts(10); + arma::Row weightCounts(10); for (size_t i = 0; i < 10; ++i) { REQUIRE((size_t) outputData(0, i) == outputLabels[i]); @@ -771,8 +771,8 @@ TEST_CASE("SparseShuffleWeightsTest", "[MathTest]") REQUIRE(outputWeights.n_elem == weights.n_elem); // Make sure we only have each point once. - arma::Row counts(10, arma::fill::zeros); - arma::Row weightCounts(10, arma::fill::zeros); + arma::Row counts(10); + arma::Row weightCounts(10); for (size_t i = 0; i < 10; ++i) { REQUIRE((size_t) outputData(0, i) == outputLabels[i]); @@ -796,7 +796,7 @@ TEST_CASE("SparseShuffleWeightsTest", "[MathTest]") */ TEST_CASE("InplaceShuffleTest", "[MathTest]") { - arma::mat data(3, 10, arma::fill::zeros); + arma::mat data(3, 10); arma::Row labels(10); for (size_t i = 0; i < 10; ++i) { @@ -814,7 +814,7 @@ TEST_CASE("InplaceShuffleTest", "[MathTest]") REQUIRE(outputLabels.n_elem == labels.n_elem); // Make sure we only have each point once. - arma::Row counts(10, arma::fill::zeros); + arma::Row counts(10); for (size_t i = 0; i < 10; ++i) { REQUIRE((size_t) outputData(0, i) == outputLabels[i]); @@ -851,7 +851,7 @@ TEST_CASE("InplaceSparseShuffleTest", "[MathTest]") REQUIRE(outputLabels.n_elem == labels.n_elem); // Make sure we only have each point once. - arma::Row counts(10, arma::fill::zeros); + arma::Row counts(10); for (size_t i = 0; i < 10; ++i) { REQUIRE((size_t) outputData(0, i) == outputLabels[i]); @@ -869,7 +869,7 @@ TEST_CASE("InplaceSparseShuffleTest", "[MathTest]") */ TEST_CASE("InplaceCubeShuffleTest", "[MathTest]") { - arma::cube data(3, 10, 5, arma::fill::zeros); + arma::cube data(3, 10, 5); arma::cube labels(1, 10, 5); for (size_t i = 0; i < labels.n_slices; ++i) { @@ -893,7 +893,7 @@ TEST_CASE("InplaceCubeShuffleTest", "[MathTest]") REQUIRE(outputLabels.n_slices == labels.n_slices); // Make sure we only have each point once. - arma::Row counts(10, arma::fill::zeros); + arma::Row counts(10); for (size_t i = 0; i < 10; ++i) { for (size_t s = 0; s < data.n_slices; ++s) @@ -914,7 +914,7 @@ TEST_CASE("InplaceCubeShuffleTest", "[MathTest]") */ TEST_CASE("InplaceShuffleWeightsTest", "[MathTest]") { - arma::mat data(3, 10, arma::fill::zeros); + arma::mat data(3, 10); arma::Row labels(10); arma::rowvec weights(10); for (size_t i = 0; i < 10; ++i) @@ -937,8 +937,8 @@ TEST_CASE("InplaceShuffleWeightsTest", "[MathTest]") REQUIRE(outputWeights.n_elem == weights.n_elem); // Make sure we only have each point once. - arma::Row counts(10, arma::fill::zeros); - arma::Row weightCounts(10, arma::fill::zeros); + arma::Row counts(10); + arma::Row weightCounts(10); for (size_t i = 0; i < 10; ++i) { REQUIRE((size_t) outputData(0, i) == outputLabels[i]); @@ -985,8 +985,8 @@ TEST_CASE("InplaceSparseShuffleWeightsTest", "[MathTest]") REQUIRE(outputWeights.n_elem == weights.n_elem); // Make sure we only have each point once. - arma::Row counts(10, arma::fill::zeros); - arma::Row weightCounts(10, arma::fill::zeros); + arma::Row counts(10); + arma::Row weightCounts(10); for (size_t i = 0; i < 10; ++i) { REQUIRE((size_t) outputData(0, i) == outputLabels[i]); diff --git a/src/mlpack/tests/scaling_test.cpp b/src/mlpack/tests/scaling_test.cpp index 9624311f34..e11fc33a7d 100644 --- a/src/mlpack/tests/scaling_test.cpp +++ b/src/mlpack/tests/scaling_test.cpp @@ -103,7 +103,7 @@ TEST_CASE("SameInputOutputTest", "[ScalingTest]") */ TEST_CASE("ZeroMatrixTest", "[ScalingTest]") { - arma::mat input(2, 4, arma::fill::zeros); + arma::mat input(2, 4); data::MeanNormalization scale; scale.Fit(input); scale.Transform(input, temp); diff --git a/src/mlpack/tests/softmax_regression_test.cpp b/src/mlpack/tests/softmax_regression_test.cpp index 9458f1db61..8492d3cb88 100644 --- a/src/mlpack/tests/softmax_regression_test.cpp +++ b/src/mlpack/tests/softmax_regression_test.cpp @@ -880,7 +880,7 @@ TEST_CASE("SoftmaxRegressionResetTest", "[SoftmaxRegressionTest]") { // Create random data. arma::mat data(50, 1000, arma::fill::randu); - arma::Row labels(1000, arma::fill::zeros); + arma::Row labels(1000); labels.subvec(500, 999).fill(1); // Create two logistic regression models. diff --git a/src/mlpack/tests/svdplusplus_test.cpp b/src/mlpack/tests/svdplusplus_test.cpp index 37bd1fb075..7c56e4a524 100644 --- a/src/mlpack/tests/svdplusplus_test.cpp +++ b/src/mlpack/tests/svdplusplus_test.cpp @@ -62,7 +62,7 @@ TEST_CASE("SVDPlusPlusEvaluate", "[SVDPlusPlusTest]") // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(rank, arma::fill::zeros); + arma::vec userVec(rank); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; @@ -368,7 +368,7 @@ TEST_CASE("SVDPlusPlusFunctionOptimize", "[SVDPlusPlusTest]") // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(rank, arma::fill::zeros); + arma::vec userVec(rank); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; @@ -406,7 +406,7 @@ TEST_CASE("SVDPlusPlusFunctionOptimize", "[SVDPlusPlusTest]") // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(rank, arma::fill::zeros); + arma::vec userVec(rank); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; @@ -475,7 +475,7 @@ TEST_CASE("SVDPlusPlusFunctionParallelOptimize", "[SVDPlusPlusTest]") // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(rank, arma::fill::zeros); + arma::vec userVec(rank); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; @@ -520,7 +520,7 @@ TEST_CASE("SVDPlusPlusFunctionParallelOptimize", "[SVDPlusPlusTest]") // Iterate through each item which the user interacted with to calculate // user vector. - arma::vec userVec(rank, arma::fill::zeros); + arma::vec userVec(rank); arma::sp_mat::const_iterator it = implicitData.begin_col(user); arma::sp_mat::const_iterator it_end = implicitData.end_col(user); size_t implicitCount = 0; From 1aef5ef569bfd869d1cae7b7837607198c6ba712 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 19 Jul 2024 16:11:42 +0200 Subject: [PATCH 153/212] Finish the last one that were on different lines Signed-off-by: Omar Shrit --- .../methods/cf/normalization/item_mean_normalization.hpp | 3 +-- .../methods/cf/normalization/user_mean_normalization.hpp | 3 +-- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 6 ++---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp b/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp index b06f47d49f..80676491d6 100644 --- a/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp +++ b/src/mlpack/methods/cf/normalization/item_mean_normalization.hpp @@ -90,8 +90,7 @@ class ItemMeanNormalization { // Calculate itemMean. itemMean = arma::vec(cleanedData.n_rows); - arma::Col ratingNum(cleanedData.n_rows, - arma::fill::zeros); + arma::Col ratingNum(cleanedData.n_rows); arma::sp_mat::iterator it = cleanedData.begin(); arma::sp_mat::iterator it_end = cleanedData.end(); for (; it != it_end; ++it) diff --git a/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp b/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp index 490a8d0604..3febb42ac3 100644 --- a/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp +++ b/src/mlpack/methods/cf/normalization/user_mean_normalization.hpp @@ -90,8 +90,7 @@ class UserMeanNormalization { // Calculate userMean. userMean = arma::vec(cleanedData.n_cols); - arma::Col ratingNum(cleanedData.n_cols, - arma::fill::zeros); + arma::Col ratingNum(cleanedData.n_cols); arma::sp_mat::iterator it = cleanedData.begin(); arma::sp_mat::iterator it_end = cleanedData.end(); for (; it != it_end; ++it) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index be283b0f94..c18b476eeb 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -43,10 +43,8 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, #pragma omp parallel { // The current state of the K-means is private for each thread - arma::mat localCentroids(centroids.n_rows, centroids.n_cols, - arma::fill::zeros); - arma::Col localCounts(centroids.n_cols, - arma::fill::zeros); + arma::mat localCentroids(centroids.n_rows, centroids.n_cols); + arma::Col localCounts(centroids.n_cols); #pragma omp for for (size_t i = 0; i < (size_t) dataset.n_cols; ++i) From 69f114affaea96045e88de193c81e639c1061d98 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 19 Jul 2024 18:12:50 +0200 Subject: [PATCH 154/212] GO back to constexpr and see if this is going to work again Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 6d3f8184cd..89c7948bc3 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -81,11 +81,11 @@ namespace mlpack { template struct GetFillType { - static const decltype(arma::fill::none) none; - static const decltype(arma::fill::zeros) zeros; - static const decltype(arma::fill::ones) ones; - static const decltype(arma::fill::randu) randu; - static const decltype(arma::fill::randn) randn; + static constexpr decltype(arma::fill::none) none = arma::fill::none; + static constexpr decltype(arma::fill::zeros) zeros = arma::fill::zeros; + static constexpr decltype(arma::fill::ones) ones = arma::fill::ones; + static constexpr decltype(arma::fill::randu) randu = arma::fill::randu; + static constexpr decltype(arma::fill::randn) randn = arma::fill::randn; }; #ifdef MLPACK_HAS_COOT @@ -94,11 +94,11 @@ namespace mlpack { typename = typename std::enable_if::value>::type*> struct GetFillType { - static const decltype(coot::fill::none) none; - static const decltype(coot::fill::zeros) zeros; - static const decltype(coot::fill::ones) ones; - static const decltype(coot::fill::randu) randu; - static const decltype(coot::fill::randn) randn; + static constexpr decltype(coot::fill::none) none = coot::fill::none; + static constexpr decltype(coot::fill::zeros) zeros = coot::fill::zeros; + static constexpr decltype(coot::fill::ones) ones = coot::fill::ones; + static constexpr decltype(coot::fill::randu) randu = coot::fill::randu; + static constexpr decltype(coot::fill::randn) randn = coot::fill::randn; }; #endif From eed380ff618329cd08ca12deadeb1e001f859b08 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 19 Jul 2024 18:22:56 +0200 Subject: [PATCH 155/212] Use only const Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 89c7948bc3..70e42b19d2 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -81,11 +81,11 @@ namespace mlpack { template struct GetFillType { - static constexpr decltype(arma::fill::none) none = arma::fill::none; - static constexpr decltype(arma::fill::zeros) zeros = arma::fill::zeros; - static constexpr decltype(arma::fill::ones) ones = arma::fill::ones; - static constexpr decltype(arma::fill::randu) randu = arma::fill::randu; - static constexpr decltype(arma::fill::randn) randn = arma::fill::randn; + const decltype(arma::fill::none) none = arma::fill::none; + const decltype(arma::fill::zeros) zeros = arma::fill::zeros; + const decltype(arma::fill::ones) ones = arma::fill::ones; + const decltype(arma::fill::randu) randu = arma::fill::randu; + const decltype(arma::fill::randn) randn = arma::fill::randn; }; #ifdef MLPACK_HAS_COOT @@ -94,11 +94,11 @@ namespace mlpack { typename = typename std::enable_if::value>::type*> struct GetFillType { - static constexpr decltype(coot::fill::none) none = coot::fill::none; - static constexpr decltype(coot::fill::zeros) zeros = coot::fill::zeros; - static constexpr decltype(coot::fill::ones) ones = coot::fill::ones; - static constexpr decltype(coot::fill::randu) randu = coot::fill::randu; - static constexpr decltype(coot::fill::randn) randn = coot::fill::randn; + const decltype(coot::fill::none) none = coot::fill::none; + const decltype(coot::fill::zeros) zeros = coot::fill::zeros; + const decltype(coot::fill::ones) ones = coot::fill::ones; + const decltype(coot::fill::randu) randu = coot::fill::randu; + const decltype(coot::fill::randn) randn = coot::fill::randn; }; #endif From f63c0ab33823910c9c18c4be0d74f7080983a384 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 19 Jul 2024 20:09:07 +0200 Subject: [PATCH 156/212] Try to use inline to see if it is going to be better Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 70e42b19d2..3034cea9c7 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -81,11 +81,11 @@ namespace mlpack { template struct GetFillType { - const decltype(arma::fill::none) none = arma::fill::none; - const decltype(arma::fill::zeros) zeros = arma::fill::zeros; - const decltype(arma::fill::ones) ones = arma::fill::ones; - const decltype(arma::fill::randu) randu = arma::fill::randu; - const decltype(arma::fill::randn) randn = arma::fill::randn; + inline static constexpr decltype(arma::fill::none) none = arma::fill::none; + inline static constexpr decltype(arma::fill::zeros) zeros = arma::fill::zeros; + inline static constexpr decltype(arma::fill::ones) ones = arma::fill::ones; + inline static constexpr decltype(arma::fill::randu) randu = arma::fill::randu; + inline static constexpr decltype(arma::fill::randn) randn = arma::fill::randn; }; #ifdef MLPACK_HAS_COOT @@ -94,11 +94,11 @@ namespace mlpack { typename = typename std::enable_if::value>::type*> struct GetFillType { - const decltype(coot::fill::none) none = coot::fill::none; - const decltype(coot::fill::zeros) zeros = coot::fill::zeros; - const decltype(coot::fill::ones) ones = coot::fill::ones; - const decltype(coot::fill::randu) randu = coot::fill::randu; - const decltype(coot::fill::randn) randn = coot::fill::randn; + inline static constexpr decltype(coot::fill::none) none = coot::fill::none; + inline static constexpr decltype(coot::fill::zeros) zeros = coot::fill::zeros; + inline static constexpr decltype(coot::fill::ones) ones = coot::fill::ones; + inline static constexpr decltype(coot::fill::randu) randu = coot::fill::randu; + inline static constexpr decltype(coot::fill::randn) randn = coot::fill::randn; }; #endif From 7e5e2736ea2332ef7e0e36d04c1d4144d833c687 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Fri, 19 Jul 2024 23:11:21 +0200 Subject: [PATCH 157/212] Apply @rcurtin suggestion Signed-off-by: Omar Shrit --- src/mlpack/core/util/using.hpp | 20 +++++++++---------- .../convolution_rules/naive_convolution.hpp | 2 +- src/mlpack/methods/ann/layer/repeat_impl.hpp | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/mlpack/core/util/using.hpp b/src/mlpack/core/util/using.hpp index 3034cea9c7..b37c826481 100644 --- a/src/mlpack/core/util/using.hpp +++ b/src/mlpack/core/util/using.hpp @@ -81,11 +81,11 @@ namespace mlpack { template struct GetFillType { - inline static constexpr decltype(arma::fill::none) none = arma::fill::none; - inline static constexpr decltype(arma::fill::zeros) zeros = arma::fill::zeros; - inline static constexpr decltype(arma::fill::ones) ones = arma::fill::ones; - inline static constexpr decltype(arma::fill::randu) randu = arma::fill::randu; - inline static constexpr decltype(arma::fill::randn) randn = arma::fill::randn; + static constexpr const decltype(arma::fill::none)& none = arma::fill::none; + static constexpr const decltype(arma::fill::zeros)& zeros = arma::fill::zeros; + static constexpr const decltype(arma::fill::ones)& ones = arma::fill::ones; + static constexpr const decltype(arma::fill::randu)& randu = arma::fill::randu; + static constexpr const decltype(arma::fill::randn)& randn = arma::fill::randn; }; #ifdef MLPACK_HAS_COOT @@ -94,11 +94,11 @@ namespace mlpack { typename = typename std::enable_if::value>::type*> struct GetFillType { - inline static constexpr decltype(coot::fill::none) none = coot::fill::none; - inline static constexpr decltype(coot::fill::zeros) zeros = coot::fill::zeros; - inline static constexpr decltype(coot::fill::ones) ones = coot::fill::ones; - inline static constexpr decltype(coot::fill::randu) randu = coot::fill::randu; - inline static constexpr decltype(coot::fill::randn) randn = coot::fill::randn; + static constexpr const decltype(coot::fill::none)& none = coot::fill::none; + static constexpr const decltype(coot::fill::zeros)& zeros = coot::fill::zeros; + static constexpr const decltype(coot::fill::ones)& ones = coot::fill::ones; + static constexpr const decltype(coot::fill::randu)& randu = coot::fill::randu; + static constexpr const decltype(coot::fill::randn)& randn = coot::fill::randn; }; #endif diff --git a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp index 20d776d426..203fcf7ab3 100644 --- a/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp +++ b/src/mlpack/methods/ann/convolution_rules/naive_convolution.hpp @@ -132,7 +132,7 @@ class NaiveConvolution // Pad filter and input to the working output shape. InMatType inputPadded(input.n_rows + 2 * paddingRows, - input.n_cols + 2 * paddingCols, GetFillType::zeros); + input.n_cols + 2 * paddingCols); inputPadded.submat(paddingRows, paddingCols, paddingRows + input.n_rows - 1, paddingCols + input.n_cols - 1) = input; diff --git a/src/mlpack/methods/ann/layer/repeat_impl.hpp b/src/mlpack/methods/ann/layer/repeat_impl.hpp index e6a35ae303..b71ff9377d 100644 --- a/src/mlpack/methods/ann/layer/repeat_impl.hpp +++ b/src/mlpack/methods/ann/layer/repeat_impl.hpp @@ -156,7 +156,7 @@ void RepeatType::ComputeOutputDimensions() // element to the input elements. This will be used in the backward // pass with a simple matrix multiplication. backIdxs.set_size(inputSize, sizeMult); - UintCol counts(inputSize, GetFillType::zeros); + UintCol counts(inputSize); for (size_t i = 0; i < outIdxs.n_elem; i++) { arma::uword r = outIdxs.at(i); From 54bb3189773466dec029e7df5920c07c0b52e114 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Mon, 22 Jul 2024 12:32:20 +0200 Subject: [PATCH 158/212] Fix the reshape of the matrix since linspace works on vectors only Signed-off-by: Omar Shrit --- src/mlpack/tests/split_data_test.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mlpack/tests/split_data_test.cpp b/src/mlpack/tests/split_data_test.cpp index 829fd0c7fe..36e912d8ea 100644 --- a/src/mlpack/tests/split_data_test.cpp +++ b/src/mlpack/tests/split_data_test.cpp @@ -94,7 +94,7 @@ void CheckDuplication(const Row& trainLabels, TEST_CASE("SplitShuffleDataResultMat", "[SplitDataTest]") { mat input(2, 10); - input = linspace(0, input.n_elem - 1); + input = reshape(linspace(0, 19, 20), 2, 10); const auto value = Split(input, 0.2); REQUIRE(std::get<0>(value).n_cols == 8); // Train data. @@ -107,7 +107,7 @@ TEST_CASE("SplitShuffleDataResultMat", "[SplitDataTest]") TEST_CASE("SplitDataResultMat", "[SplitDataTest]") { mat input(2, 10); - input = linspace(0, input.n_elem - 1); + input = reshape(linspace(0, 19 ,20), 2, 10); const auto value = Split(input, 0.2, false); REQUIRE(std::get<0>(value).n_cols == 8); // Train data. @@ -121,7 +121,7 @@ TEST_CASE("SplitDataResultMat", "[SplitDataTest]") TEST_CASE("ZeroRatioSplitData", "[SplitDataTest]") { mat input(2, 10); - input = linspace(0, input.n_elem - 1); + input = reshape(linspace(0, 19, 20), 2, 10); const auto value = Split(input, 0, false); REQUIRE(std::get<0>(value).n_cols == 10); // Train data. @@ -135,7 +135,7 @@ TEST_CASE("ZeroRatioSplitData", "[SplitDataTest]") TEST_CASE("TotalRatioSplitData", "[SplitDataTest]") { mat input(2, 10); - input = linspace(0, input.n_elem - 1); + input = reshape(linspace(0, 19, 20), 2, 10); const auto value = Split(input, 1, false); REQUIRE(std::get<0>(value).n_cols == 0); // Train data. @@ -189,7 +189,7 @@ TEST_CASE("SplitCheckSize", "[SplitDataTest]") TEST_CASE("SplitDataLargerTest", "[SplitDataTest]") { mat input(10, 497); - input = linspace(0, input.n_elem - 1); + input = reshape(linspace(0, 4969, 4970), 10, 497); const auto value = Split(input, 0.3); REQUIRE(std::get<0>(value).n_cols == 497 - size_t(0.3 * 497)); From 25b3537f63a91ad28100008d44233235d94ac19a Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 22 Jul 2024 20:01:52 +0200 Subject: [PATCH 159/212] optimization and fix --- .../methods/kmeans/naive_kmeans_impl.hpp | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index e73c4eb532..a4f898369f 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -70,28 +70,15 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, double minDistance = std::numeric_limits::max(); size_t closestCluster = clusters; - const double* dataPoint = dataset.colptr(i); - double dataNorm = 0.0; - - // Compute data point norm - #pragma omp simd reduction(+:dataNorm) - for (size_t d = 0; d < dims; ++d) - { - dataNorm += dataPoint[d] * dataPoint[d]; - } + // Handle both dense and sparse matrices + auto dataPoint = dataset.col(i); + double dataNorm = arma::dot(dataPoint, dataPoint); // Find closest centroid for (size_t j = 0; j < clusters; ++j) { - const double* centroid = centroids.colptr(j); - double dotProduct = 0.0; - - // Compute dot product - #pragma omp simd reduction(+:dotProduct) - for (size_t d = 0; d < dims; ++d) - { - dotProduct += dataPoint[d] * centroid[d]; - } + const arma::vec& centroid = centroids.col(j); + double dotProduct = arma::dot(dataPoint, centroid); // Squared Euclidean distance double dist = dataNorm + centroidNorms(j) - 2 * dotProduct; @@ -104,12 +91,7 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, } // Update local centroids and counts - double* localCentroidCol = localCentroids.colptr(closestCluster); - #pragma omp simd - for (size_t d = 0; d < dims; ++d) - { - localCentroidCol[d] += dataPoint[d]; - } + localCentroids.col(closestCluster) += dataPoint; localCounts(closestCluster)++; } @@ -122,7 +104,6 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, } // Normalize the centroids - #pragma omp parallel for schedule(static) for (size_t j = 0; j < clusters; ++j) { if (counts(j) > 0) From f9c9aaf0c3f52cf56a1485c563a0060735d9a27a Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 22 Jul 2024 20:04:52 +0200 Subject: [PATCH 160/212] ifdef USE_OPENMP --- .../methods/kmeans/naive_kmeans_impl.hpp | 79 +++++++++++-------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index a4f898369f..700c462d8f 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -19,6 +19,10 @@ // In case it hasn't been included yet. #include "naive_kmeans.hpp" +#ifdef MLPACK_USE_OPENMP + #include +#endif + namespace mlpack { template @@ -29,7 +33,6 @@ NaiveKMeans::NaiveKMeans(const MatType& dataset, distanceCalculations(0) { /* Nothing to do. */ } -// Run a single iteration. template double NaiveKMeans::Iterate(const arma::mat& centroids, arma::mat& newCentroids, @@ -44,63 +47,69 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, // Pre-compute squared norms of centroids arma::vec centroidNorms(clusters); - #pragma omp parallel for schedule(static) + #ifdef MLPACK_USE_OPENMP + #pragma omp parallel for schedule(static) + #endif for (size_t j = 0; j < clusters; ++j) { centroidNorms(j) = arma::dot(centroids.col(j), centroids.col(j)); } // Determine the number of threads and calculate segment size - const size_t numThreads = static_cast(std::max(1, omp_get_max_threads())); - const size_t minVectorsPerThread = 100; - const size_t effectiveThreads = std::min(numThreads, points / minVectorsPerThread); + size_t effectiveThreads = 1; + #ifdef MLPACK_USE_OPENMP + const size_t numThreads = static_cast(std::max(1, omp_get_max_threads())); + const size_t minVectorsPerThread = 100; + effectiveThreads = std::min(numThreads, points / minVectorsPerThread); + #endif const size_t nominalSegmentSize = points / effectiveThreads; - #pragma omp parallel num_threads(effectiveThreads) - { - arma::mat localCentroids(dims, clusters, arma::fill::zeros); - arma::Col localCounts(clusters, arma::fill::zeros); + // Pre-allocate thread-local storage + std::vector threadCentroids(effectiveThreads, arma::mat(dims, clusters, arma::fill::zeros)); + std::vector> threadCounts(effectiveThreads, arma::Col(clusters, arma::fill::zeros)); - const size_t threadId = omp_get_thread_num(); + #ifdef MLPACK_USE_OPENMP + #pragma omp parallel num_threads(effectiveThreads) + #endif + { + size_t threadId = 0; + #ifdef MLPACK_USE_OPENMP + threadId = omp_get_thread_num(); + #endif const size_t segmentStart = threadId * nominalSegmentSize; const size_t segmentEnd = (threadId == effectiveThreads - 1) ? points : (threadId + 1) * nominalSegmentSize; + arma::mat& localCentroids = threadCentroids[threadId]; + arma::Col& localCounts = threadCounts[threadId]; + + arma::vec distances(clusters); + for (size_t i = segmentStart; i < segmentEnd; ++i) { - double minDistance = std::numeric_limits::max(); - size_t closestCluster = clusters; + const auto dataPoint = dataset.col(i); + const double dataNorm = arma::dot(dataPoint, dataPoint); - // Handle both dense and sparse matrices - auto dataPoint = dataset.col(i); - double dataNorm = arma::dot(dataPoint, dataPoint); - - // Find closest centroid + // Calculate distances to all centroids for (size_t j = 0; j < clusters; ++j) { const arma::vec& centroid = centroids.col(j); - double dotProduct = arma::dot(dataPoint, centroid); - - // Squared Euclidean distance - double dist = dataNorm + centroidNorms(j) - 2 * dotProduct; - - if (dist < minDistance) - { - minDistance = dist; - closestCluster = j; - } + distances(j) = dataNorm + centroidNorms(j) - 2 * arma::dot(dataPoint, centroid); } + // Find the closest centroid + const size_t closestCluster = distances.index_min(); + // Update local centroids and counts localCentroids.col(closestCluster) += dataPoint; localCounts(closestCluster)++; } + } - // Combine results - #pragma omp critical - { - newCentroids += localCentroids; - counts += localCounts; - } + // Combine results from all threads + for (size_t t = 0; t < effectiveThreads; ++t) + { + newCentroids += threadCentroids[t]; + counts += threadCounts[t]; } // Normalize the centroids @@ -114,7 +123,9 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, // Calculate cluster distortion double cNorm = 0.0; - #pragma omp parallel for reduction(+:cNorm) schedule(static) + #ifdef MLPACK_USE_OPENMP + #pragma omp parallel for reduction(+:cNorm) schedule(static) + #endif for (size_t j = 0; j < clusters; ++j) { cNorm += arma::norm(centroids.col(j) - newCentroids.col(j), 2); From 8fa6866cff437ebc282be221ac53d4f95623f562 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 22 Jul 2024 20:20:20 +0200 Subject: [PATCH 161/212] fix and optimization --- .../methods/kmeans/hamerly_kmeans_impl.hpp | 97 +++++++++++++------ 1 file changed, 67 insertions(+), 30 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index f69eb4c8e5..39cd2727c0 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -15,6 +15,10 @@ // In case it hasn't been included yet. #include "hamerly_kmeans.hpp" +#ifdef MLPACK_USE_OPENMP + #include +#endif + namespace mlpack { template @@ -50,7 +54,9 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // Calculate minimum intra-cluster distance for each cluster. minClusterDistances.fill(DBL_MAX); - #pragma omp parallel for reduction(+:distanceCalculations) + #ifdef MLPACK_USE_OPENMP + #pragma omp parallel for reduction(+:distanceCalculations) + #endif for (size_t i = 0; i < centroids.n_cols; ++i) { for (size_t j = i + 1; j < centroids.n_cols; ++j) @@ -60,15 +66,19 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, ++distanceCalculations; // Update bounds, if this intra-cluster distance is smaller. - #pragma omp atomic - minClusterDistances(i) = std::min(minClusterDistances(i), dist); - #pragma omp atomic - minClusterDistances(j) = std::min(minClusterDistances(j), dist); + #ifdef MLPACK_USE_OPENMP + #pragma omp critical + #endif + { + minClusterDistances(i) = std::min(minClusterDistances(i), dist); + minClusterDistances(j) = std::min(minClusterDistances(j), dist); + } } } - #pragma omp parallel for reduction(+:distanceCalculations, hamerlyPruned) \ - reduction(+:newCentroids, counts) + #ifdef MLPACK_USE_OPENMP + #pragma omp parallel for reduction(+:distanceCalculations, hamerlyPruned) + #endif for (size_t i = 0; i < dataset.n_cols; ++i) { const double m = std::max(minClusterDistances(assignments[i]), @@ -78,8 +88,13 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, if (upperBounds(i) <= m) { ++hamerlyPruned; - newCentroids.col(assignments[i]) += dataset.col(i); - ++counts(assignments[i]); + #ifdef MLPACK_USE_OPENMP + #pragma omp critical + #endif + { + newCentroids.col(assignments[i]) += dataset.col(i); + ++counts(assignments[i]); + } continue; } @@ -91,8 +106,13 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // Second bound test. if (upperBounds(i) <= m) { - newCentroids.col(assignments[i]) += dataset.col(i); - ++counts(assignments[i]); + #ifdef MLPACK_USE_OPENMP + #pragma omp critical + #endif + { + newCentroids.col(assignments[i]) += dataset.col(i); + ++counts(assignments[i]); + } continue; } @@ -100,6 +120,10 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // This is Hamerly's Point-All-Ctrs() function from the paper. // We have to reset the lower bound first. lowerBounds(i) = DBL_MAX; + size_t newAssignment = assignments[i]; + double newUpperBound = upperBounds(i); + double newLowerBound = DBL_MAX; + for (size_t c = 0; c < centroids.n_cols; ++c) { if (c == assignments[i]) @@ -107,37 +131,46 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, const double dist = distance.Evaluate(dataset.col(i), centroids.col(c)); - // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)). - if (dist < upperBounds(i)) + // Is this a better cluster? + if (dist < newUpperBound) { - // lowerBounds holds the second closest cluster. - lowerBounds(i) = upperBounds(i); - upperBounds(i) = dist; - assignments[i] = c; + newLowerBound = newUpperBound; + newUpperBound = dist; + newAssignment = c; } - else if (dist < lowerBounds(i)) + else if (dist < newLowerBound) { - // This is a closer second-closest cluster. - lowerBounds(i) = dist; + newLowerBound = dist; } } distanceCalculations += centroids.n_cols - 1; + // Update bounds and assignment + upperBounds(i) = newUpperBound; + lowerBounds(i) = newLowerBound; + assignments[i] = newAssignment; + // Update new centroids. - newCentroids.col(assignments[i]) += dataset.col(i); - ++counts(assignments[i]); + #ifdef MLPACK_USE_OPENMP + #pragma omp critical + #endif + { + newCentroids.col(newAssignment) += dataset.col(i); + ++counts(newAssignment); + } } - // Normalize centroids and calculate cluster movement (contains parts of - // Move-Centers() and Update-Bounds()). + // Normalize centroids and calculate cluster movement double furthestMovement = 0.0; double secondFurthestMovement = 0.0; size_t furthestMovingCluster = 0; arma::vec centroidMovements(centroids.n_cols); double centroidMovement = 0.0; - #pragma omp parallel for reduction(+:distanceCalculations, centroidMovement) \ - reduction(max:furthestMovement) + #ifdef MLPACK_USE_OPENMP + #pragma omp parallel for reduction(+:distanceCalculations, centroidMovement) \ + reduction(max:furthestMovement) + #endif for (size_t c = 0; c < centroids.n_cols; ++c) { if (counts(c) > 0) @@ -152,7 +185,9 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, if (movement > furthestMovement) { - #pragma omp critical + #ifdef MLPACK_USE_OPENMP + #pragma omp critical + #endif { if (movement > furthestMovement) { @@ -168,8 +203,10 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, } } - // Now update bounds (lines 3-8 of Update-Bounds()). - #pragma omp parallel for + // Now update bounds + #ifdef MLPACK_USE_OPENMP + #pragma omp parallel for + #endif for (size_t i = 0; i < dataset.n_cols; ++i) { upperBounds(i) += centroidMovements(assignments[i]); @@ -186,4 +223,4 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, } // namespace mlpack -#endif +#endif \ No newline at end of file From 4778f7b0b073343aa0b76ec8f4fd9ab0512c7331 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 22 Jul 2024 22:18:18 +0200 Subject: [PATCH 162/212] fix --- .../methods/kmeans/hamerly_kmeans_impl.hpp | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index 39cd2727c0..4316d4802b 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -30,12 +30,12 @@ HamerlyKMeans::HamerlyKMeans(const MatType& dataset, { // Nothing to do. } - -template + template double HamerlyKMeans::Iterate(const arma::mat& centroids, arma::mat& newCentroids, arma::Col& counts) { + static constexpr double eps = std::numeric_limits::epsilon(); size_t hamerlyPruned = 0; // If this is the first iteration, we need to set all the bounds. @@ -61,17 +61,20 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, { for (size_t j = i + 1; j < centroids.n_cols; ++j) { - const double dist = distance.Evaluate(centroids.col(i), - centroids.col(j)) / 2.0; + const double dist = distance.Evaluate(centroids.col(i), centroids.col(j)); ++distanceCalculations; - // Update bounds, if this intra-cluster distance is smaller. - #ifdef MLPACK_USE_OPENMP - #pragma omp critical - #endif + if (dist > eps) { - minClusterDistances(i) = std::min(minClusterDistances(i), dist); - minClusterDistances(j) = std::min(minClusterDistances(j), dist); + const double halfDist = dist / 2.0; + // Update bounds, if this intra-cluster distance is smaller. + #ifdef MLPACK_USE_OPENMP + #pragma omp critical + #endif + { + minClusterDistances(i) = std::min(minClusterDistances(i), halfDist); + minClusterDistances(j) = std::min(minClusterDistances(j), halfDist); + } } } } @@ -85,7 +88,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, lowerBounds(i)); // First bound test. - if (upperBounds(i) <= m) + if (upperBounds(i) <= m + eps) { ++hamerlyPruned; #ifdef MLPACK_USE_OPENMP @@ -104,7 +107,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, ++distanceCalculations; // Second bound test. - if (upperBounds(i) <= m) + if (upperBounds(i) <= m + eps) { #ifdef MLPACK_USE_OPENMP #pragma omp critical @@ -175,10 +178,11 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, { if (counts(c) > 0) newCentroids.col(c) /= counts(c); + else + newCentroids.col(c) = centroids.col(c); // Calculate movement. - const double movement = distance.Evaluate(centroids.col(c), - newCentroids.col(c)); + const double movement = std::sqrt(arma::sum(arma::square(centroids.col(c) - newCentroids.col(c)))); centroidMovements(c) = movement; centroidMovement += std::pow(movement, 2.0); ++distanceCalculations; @@ -209,18 +213,30 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, #endif for (size_t i = 0; i < dataset.n_cols; ++i) { - upperBounds(i) += centroidMovements(assignments[i]); - if (assignments[i] == furthestMovingCluster) - lowerBounds(i) -= secondFurthestMovement; + if (assignments[i] < centroids.n_cols) + { + upperBounds(i) += centroidMovements(assignments[i]); + if (assignments[i] == furthestMovingCluster) + lowerBounds(i) -= secondFurthestMovement; + else + lowerBounds(i) -= furthestMovement; + } else - lowerBounds(i) -= furthestMovement; + { + // Handle invalid assignment + #ifdef MLPACK_USE_OPENMP + #pragma omp critical + #endif + { + Log::Warn << "Invalid assignment for point " << i << std::endl; + } + } } Log::Info << "Hamerly prunes: " << hamerlyPruned << ".\n"; return std::sqrt(centroidMovement); } - } // namespace mlpack #endif \ No newline at end of file From 61f6adb319cc330187a9bfcb1137892edae37bd8 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 22 Jul 2024 22:27:59 +0200 Subject: [PATCH 163/212] fix --- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 700c462d8f..df81abdfc2 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -93,7 +93,7 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, for (size_t j = 0; j < clusters; ++j) { const arma::vec& centroid = centroids.col(j); - distances(j) = dataNorm + centroidNorms(j) - 2 * arma::dot(dataPoint, centroid); + distances(j) = std::max(0.0, dataNorm + centroidNorms(j) - 2 * arma::dot(dataPoint, centroid)); } // Find the closest centroid @@ -112,13 +112,19 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, counts += threadCounts[t]; } + const double eps = std::numeric_limits::epsilon(); // Normalize the centroids for (size_t j = 0; j < clusters; ++j) { - if (counts(j) > 0) + if (counts(j) > eps) { newCentroids.col(j) /= counts(j); } + else + { + // Handle empty or near-empty cluster + newCentroids.col(j) = centroids.col(j); + } } // Calculate cluster distortion From 43ccba41c366f3620f16055533751fac31f81ed2 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 23 Jul 2024 18:35:31 -0400 Subject: [PATCH 164/212] Update doc/user/methods/mean_shift.md Co-authored-by: Himanshu Pathak --- doc/user/methods/mean_shift.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/methods/mean_shift.md b/doc/user/methods/mean_shift.md index e67ea48cef..3725abb700 100644 --- a/doc/user/methods/mean_shift.md +++ b/doc/user/methods/mean_shift.md @@ -327,7 +327,7 @@ MeanShift to the flat kernel, or, setting `UseKernel = false`)* - [`TriangularKernel`](../core.md#triangularkernel) -Custom kernels can be easily implemented, and must implement only one function +Custom kernels for mean shift can be easily implemented, and must implement only one function (`Gradient()`): ```c++ From 9a1b1b62a7ccb7853e8294b51b57dc3cfc7d4d1d Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 23 Jul 2024 18:35:50 -0400 Subject: [PATCH 165/212] Update src/mlpack/methods/mean_shift/mean_shift.hpp Co-authored-by: Omar Shrit --- src/mlpack/methods/mean_shift/mean_shift.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/mean_shift/mean_shift.hpp b/src/mlpack/methods/mean_shift/mean_shift.hpp index 6746728a15..6316daa8b6 100644 --- a/src/mlpack/methods/mean_shift/mean_shift.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift.hpp @@ -138,7 +138,7 @@ class MeanShift * @param data The reference data set. * @param binSize Width of hypercube bins. * @param minFreq Minimum number of points in bin. - * @param seed Matrix to store generated seeds in. + * @param seeds Matrix to store generated seeds in. */ template void GenSeeds(const MatType& data, From 309033d624cae69d666b3038452abf190ba2445b Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 23 Jul 2024 18:38:39 -0400 Subject: [PATCH 166/212] Minor line length fix. --- doc/user/methods/mean_shift.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/user/methods/mean_shift.md b/doc/user/methods/mean_shift.md index 3725abb700..a885e45e07 100644 --- a/doc/user/methods/mean_shift.md +++ b/doc/user/methods/mean_shift.md @@ -327,8 +327,8 @@ MeanShift to the flat kernel, or, setting `UseKernel = false`)* - [`TriangularKernel`](../core.md#triangularkernel) -Custom kernels for mean shift can be easily implemented, and must implement only one function -(`Gradient()`): +Custom kernels for mean shift can be easily implemented, and must implement only +one function (`Gradient()`): ```c++ class CustomKernel From d04ce6fa5be5142956055dc4ef8d8ec947aa76d0 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 24 Jul 2024 09:31:01 +0200 Subject: [PATCH 167/212] Update src/mlpack/methods/lsh/lsh_search_impl.hpp Co-authored-by: Ryan Curtin --- src/mlpack/methods/lsh/lsh_search_impl.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mlpack/methods/lsh/lsh_search_impl.hpp b/src/mlpack/methods/lsh/lsh_search_impl.hpp index 9d9c3be8a2..151c84f238 100644 --- a/src/mlpack/methods/lsh/lsh_search_impl.hpp +++ b/src/mlpack/methods/lsh/lsh_search_impl.hpp @@ -299,8 +299,7 @@ void LSHSearch::Train(MatType referenceSet, // 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); + arma::Row secondHashBinCounts(secondHashSize); for (size_t i = 0; i < secondHashVectors.n_elem; ++i) secondHashBinCounts[secondHashVectors[i]]++; From f123b3b0c3b5327a8f0df8251703466f2a11288e Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 24 Jul 2024 09:31:14 +0200 Subject: [PATCH 168/212] Update src/mlpack/methods/lars/lars_impl.hpp Co-authored-by: Ryan Curtin --- src/mlpack/methods/lars/lars_impl.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mlpack/methods/lars/lars_impl.hpp b/src/mlpack/methods/lars/lars_impl.hpp index 2c850a064d..eb1c260c2c 100644 --- a/src/mlpack/methods/lars/lars_impl.hpp +++ b/src/mlpack/methods/lars/lars_impl.hpp @@ -575,8 +575,7 @@ LARS::Train(const MatType& matX, // Initialize yHat and beta. arma::Col beta(dataRef.n_cols); arma::Col yHat(dataRef.n_rows); - arma::Col yHatDirection(dataRef.n_rows, - arma::fill::none); + arma::Col yHatDirection(dataRef.n_rows, arma::fill::none); bool lassocond = false; From 97f903e75efdb0e7bed34f23118557089b42a4ea Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 24 Jul 2024 09:31:26 +0200 Subject: [PATCH 169/212] Update src/mlpack/methods/lmnn/constraints_impl.hpp Co-authored-by: Ryan Curtin --- src/mlpack/methods/lmnn/constraints_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/lmnn/constraints_impl.hpp b/src/mlpack/methods/lmnn/constraints_impl.hpp index dd5622386d..74014f061e 100644 --- a/src/mlpack/methods/lmnn/constraints_impl.hpp +++ b/src/mlpack/methods/lmnn/constraints_impl.hpp @@ -405,7 +405,7 @@ void Constraints::Triplets( UMatType targetNeighbors(k, dataset.n_cols);; TargetNeighbors(targetNeighbors, dataset, labels, norms); - outputMatrix = UMatType(3, k * k * N ); + outputMatrix = UMatType(3, k * k * N); #pragma omp parallel for collapse(3) for (size_t i = 0; i < N; ++i) From 9876e255696b23d7a77df4c9840f56d94381a253 Mon Sep 17 00:00:00 2001 From: Omar Shrit Date: Wed, 24 Jul 2024 22:23:54 +0200 Subject: [PATCH 170/212] Fix the typo Signed-off-by: Omar Shrit --- src/mlpack/tests/split_data_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/tests/split_data_test.cpp b/src/mlpack/tests/split_data_test.cpp index 36e912d8ea..0293944805 100644 --- a/src/mlpack/tests/split_data_test.cpp +++ b/src/mlpack/tests/split_data_test.cpp @@ -358,7 +358,7 @@ TEST_CASE("SplitDataResultField", "[SplitDataTest]") mat matB(2, 10); matA = linspace(0, matA.n_elem - 1); - matA = linspace(matA.n_elem, matA.n_elem + matB.n_elem - 1); + matB = linspace(matA.n_elem, matA.n_elem + matB.n_elem - 1); input(0, 0) = matA; input(0, 1) = matB; From 8b93069bac7a918fafb9adfa65be5f30cdf26fc0 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 25 Jul 2024 01:43:30 -0400 Subject: [PATCH 171/212] Specify label for stale PRs. (#3775) --- .github/workflows/stale.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 60545c7cfd..8f59d84fbf 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -15,6 +15,7 @@ jobs: days-before-issues-stale: 30 days-before-issue-close: 7 stale-issue-label: "s: stale" + stale-pr-label: "s: stale" stale-issue-message: "This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions! :+1:" days-before-pr-stale: 30, days-before-pr-close: 14 From c8945e3f030988af68c1f9598b0f91c81a740992 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Fri, 26 Jul 2024 18:19:25 +0200 Subject: [PATCH 172/212] updates based on the comments --- .../methods/kmeans/naive_kmeans_impl.hpp | 122 ++++++++---------- 1 file changed, 52 insertions(+), 70 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index df81abdfc2..940998dd3c 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -19,10 +19,6 @@ // In case it hasn't been included yet. #include "naive_kmeans.hpp" -#ifdef MLPACK_USE_OPENMP - #include -#endif - namespace mlpack { template @@ -45,101 +41,87 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, newCentroids.zeros(dims, clusters); counts.zeros(clusters); - // Pre-compute squared norms of centroids - arma::vec centroidNorms(clusters); - #ifdef MLPACK_USE_OPENMP - #pragma omp parallel for schedule(static) - #endif - for (size_t j = 0; j < clusters; ++j) + // Pre-compute centroid norms if using Euclidean distance + arma::vec centroidNorms; + if (std::is_same::value) { - centroidNorms(j) = arma::dot(centroids.col(j), centroids.col(j)); + centroidNorms = arma::sum(arma::square(centroids), 0).t(); } - // Determine the number of threads and calculate segment size - size_t effectiveThreads = 1; - #ifdef MLPACK_USE_OPENMP - const size_t numThreads = static_cast(std::max(1, omp_get_max_threads())); - const size_t minVectorsPerThread = 100; - effectiveThreads = std::min(numThreads, points / minVectorsPerThread); - #endif - const size_t nominalSegmentSize = points / effectiveThreads; - - // Pre-allocate thread-local storage - std::vector threadCentroids(effectiveThreads, arma::mat(dims, clusters, arma::fill::zeros)); - std::vector> threadCounts(effectiveThreads, arma::Col(clusters, arma::fill::zeros)); - - #ifdef MLPACK_USE_OPENMP - #pragma omp parallel num_threads(effectiveThreads) - #endif + #pragma omp parallel { - size_t threadId = 0; - #ifdef MLPACK_USE_OPENMP - threadId = omp_get_thread_num(); - #endif - const size_t segmentStart = threadId * nominalSegmentSize; - const size_t segmentEnd = (threadId == effectiveThreads - 1) ? points : (threadId + 1) * nominalSegmentSize; + // Thread-local storage + arma::mat localNewCentroids(dims, clusters, arma::fill::zeros); + arma::Col localCounts(clusters, arma::fill::zeros); - arma::mat& localCentroids = threadCentroids[threadId]; - arma::Col& localCounts = threadCounts[threadId]; - - arma::vec distances(clusters); - - for (size_t i = segmentStart; i < segmentEnd; ++i) + #pragma omp for schedule(static) + for (size_t i = 0; i < points; ++i) { - const auto dataPoint = dataset.col(i); - const double dataNorm = arma::dot(dataPoint, dataPoint); + size_t closestCluster = 0; + double minDistance = std::numeric_limits::max(); + const auto& point = dataset.col(i); - // Calculate distances to all centroids - for (size_t j = 0; j < clusters; ++j) + if (std::is_same::value) { - const arma::vec& centroid = centroids.col(j); - distances(j) = std::max(0.0, dataNorm + centroidNorms(j) - 2 * arma::dot(dataPoint, centroid)); + // Optimized Euclidean distance calculation + const double pointNorm = arma::dot(point, point); + for (size_t j = 0; j < clusters; ++j) + { + const double dist = pointNorm + centroidNorms[j] - 2 * arma::dot(point, centroids.col(j)); + if (dist < minDistance) + { + minDistance = dist; + closestCluster = j; + } + } + } + else + { + // General distance metric + for (size_t j = 0; j < clusters; ++j) + { + const double dist = distance.Evaluate(point, centroids.col(j)); + if (dist < minDistance) + { + minDistance = dist; + closestCluster = j; + } + } } - // Find the closest centroid - const size_t closestCluster = distances.index_min(); - // Update local centroids and counts - localCentroids.col(closestCluster) += dataPoint; - localCounts(closestCluster)++; + localNewCentroids.col(closestCluster) += point; + localCounts[closestCluster]++; + } + + // Combine results + #pragma omp critical + { + newCentroids += localNewCentroids; + counts += localCounts; } } - // Combine results from all threads - for (size_t t = 0; t < effectiveThreads; ++t) - { - newCentroids += threadCentroids[t]; - counts += threadCounts[t]; - } - - const double eps = std::numeric_limits::epsilon(); // Normalize the centroids for (size_t j = 0; j < clusters; ++j) { - if (counts(j) > eps) - { - newCentroids.col(j) /= counts(j); - } + if (counts[j] > 0) + newCentroids.col(j) /= counts[j]; else - { - // Handle empty or near-empty cluster newCentroids.col(j) = centroids.col(j); - } } // Calculate cluster distortion double cNorm = 0.0; - #ifdef MLPACK_USE_OPENMP - #pragma omp parallel for reduction(+:cNorm) schedule(static) - #endif + #pragma omp parallel for reduction(+:cNorm) schedule(static) for (size_t j = 0; j < clusters; ++j) { - cNorm += arma::norm(centroids.col(j) - newCentroids.col(j), 2); + cNorm += std::pow(arma::norm(centroids.col(j) - newCentroids.col(j)), 2); } distanceCalculations += clusters * points; - return cNorm; + return std::sqrt(cNorm); } } // namespace mlpack From 06ed29551e95000336dbc4f2cf60335f5fac78a5 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Wed, 17 Jul 2024 13:32:00 +0100 Subject: [PATCH 173/212] updated some errors from previous pr on adapting nearest interpolation --- HISTORY.md | 1 + src/mlpack/methods/ann/layer/nearest_interpolation.hpp | 3 ++- src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 3efc2e024d..0ce71722b7 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -14,6 +14,7 @@ _????-??-??_ * Bump minimum Armadillo version to 10.8 (#3760). + * Adapt `NearestInterpolation` ANN layer to new Layer Inteface ## mlpack 4.4.0 _2024-05-26_ diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp index 684e8510f1..1d9d0141a4 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp @@ -36,7 +36,8 @@ class NearestInterpolationType : public Layer NearestInterpolationType(); /**Create NearestInterpolation Object with the same scaleFactor along - * each dimension + * each dimension. + * NOTE: Currently this only supports 2 scaleFactors, and we plan to generalize to 1d, 2d and 3d in the future. * * @param scaleFactor Scale factors to scale each dimension by. */ diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index 14d2e30ff5..2969ed787a 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -149,8 +149,8 @@ void NearestInterpolationType::Backward( template void NearestInterpolationType::ComputeOutputDimensions() { - if (this->inputDimensions.size() - 1 != scaleFactors.size()) { - throw std::runtime_error("Scale factors must match number of rows and columns."); + if (this->inputDimensions.size() < scaleFactors.size()) { + throw std::runtime_error("Insufficient number of input dimensions."); } this->outputDimensions = this->inputDimensions; for (size_t i = 0; i < this->InputDimensions().size()-1; i++) From 7aafdc5b2985b7ce298ed7d74d26c822df90aa9f Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Sun, 21 Jul 2024 15:03:56 +0100 Subject: [PATCH 174/212] added pr number --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 0ce71722b7..3245eb9181 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -14,7 +14,7 @@ _????-??-??_ * Bump minimum Armadillo version to 10.8 (#3760). - * Adapt `NearestInterpolation` ANN layer to new Layer Inteface + * Adapt `NearestInterpolation` ANN layer to new Layer Inteface (#3768). ## mlpack 4.4.0 _2024-05-26_ From 538bf9f324d709f992a40dbb88de5b41d476f552 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Mon, 29 Jul 2024 09:44:30 +0100 Subject: [PATCH 175/212] style fix --- src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index 2969ed787a..1523ec433d 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -153,9 +153,10 @@ void NearestInterpolationType::ComputeOutputDimensions() throw std::runtime_error("Insufficient number of input dimensions."); } this->outputDimensions = this->inputDimensions; - for (size_t i = 0; i < this->InputDimensions().size()-1; i++) + for (size_t i = 0; i < this->InputDimensions().size() - 1; i++) { - this->outputDimensions[i] = std::round((double)this->outputDimensions[i] * scaleFactors[i]); + this->outputDimensions[i] = std::round( + (double)this->outputDimensions[i] * scaleFactors[i]); } } From 9b31b5b3808e14918d1f9a3098e6a5ec050a3eea Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Mon, 29 Jul 2024 09:44:53 +0100 Subject: [PATCH 176/212] updated comment with more explanation on input and output dimension sizes --- src/mlpack/methods/ann/layer/nearest_interpolation.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp index 1d9d0141a4..bc9041a2fb 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation.hpp @@ -37,7 +37,12 @@ class NearestInterpolationType : public Layer /**Create NearestInterpolation Object with the same scaleFactor along * each dimension. - * NOTE: Currently this only supports 2 scaleFactors, and we plan to generalize to 1d, 2d and 3d in the future. + * NOTE: scaleFactors must be a two element vector, the first element + * for scaling the first dimension and the second element for scaling + * the second dimension. + * + * If the input dimensions are n x m x ..., then the output dimensions + * will be (n x scaleFactors[0]) x (m x scaleFactors[1]) x ... * * @param scaleFactor Scale factors to scale each dimension by. */ From dbe068040d40372e0e976dc7309fcaf4cb582031 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Mon, 29 Jul 2024 09:45:08 +0100 Subject: [PATCH 177/212] added newline to HISTORY.md --- HISTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.md b/HISTORY.md index 3245eb9181..c414ed983a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -15,6 +15,7 @@ _????-??-??_ * Bump minimum Armadillo version to 10.8 (#3760). * Adapt `NearestInterpolation` ANN layer to new Layer Inteface (#3768). + ## mlpack 4.4.0 _2024-05-26_ From 38a696de4407996f42746ee995b6593c66f7e4f5 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 29 Jul 2024 14:06:30 +0200 Subject: [PATCH 178/212] fix and optimizations --- .../methods/kmeans/hamerly_kmeans_impl.hpp | 211 +++++++++--------- 1 file changed, 101 insertions(+), 110 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index 4316d4802b..7a984549ca 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -15,10 +15,6 @@ // In case it hasn't been included yet. #include "hamerly_kmeans.hpp" -#ifdef MLPACK_USE_OPENMP - #include -#endif - namespace mlpack { template @@ -30,7 +26,7 @@ HamerlyKMeans::HamerlyKMeans(const MatType& dataset, { // Nothing to do. } - template +template double HamerlyKMeans::Iterate(const arma::mat& centroids, arma::mat& newCentroids, arma::Col& counts) @@ -54,112 +50,117 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // Calculate minimum intra-cluster distance for each cluster. minClusterDistances.fill(DBL_MAX); - #ifdef MLPACK_USE_OPENMP - #pragma omp parallel for reduction(+:distanceCalculations) - #endif - for (size_t i = 0; i < centroids.n_cols; ++i) + + #pragma omp parallel { - for (size_t j = i + 1; j < centroids.n_cols; ++j) + arma::vec localMinClusterDistances(centroids.n_cols, arma::fill::value(DBL_MAX)); + #pragma omp for reduction(+:distanceCalculations) schedule(dynamic) + for (size_t i = 0; i < centroids.n_cols; ++i) { - const double dist = distance.Evaluate(centroids.col(i), centroids.col(j)); - ++distanceCalculations; - - if (dist > eps) + for (size_t j = i + 1; j < centroids.n_cols; ++j) { - const double halfDist = dist / 2.0; - // Update bounds, if this intra-cluster distance is smaller. - #ifdef MLPACK_USE_OPENMP - #pragma omp critical - #endif + const double dist = distance.Evaluate(centroids.col(i), centroids.col(j)); + ++distanceCalculations; + + if (dist > eps) { - minClusterDistances(i) = std::min(minClusterDistances(i), halfDist); - minClusterDistances(j) = std::min(minClusterDistances(j), halfDist); + const double halfDist = dist / 2.0; + localMinClusterDistances(i) = std::min(localMinClusterDistances(i), halfDist); + localMinClusterDistances(j) = std::min(localMinClusterDistances(j), halfDist); } } } + + #pragma omp critical + { + for (size_t i = 0; i < centroids.n_cols; ++i) + { + minClusterDistances(i) = std::min(minClusterDistances(i), localMinClusterDistances(i)); + } + } } - #ifdef MLPACK_USE_OPENMP - #pragma omp parallel for reduction(+:distanceCalculations, hamerlyPruned) - #endif - for (size_t i = 0; i < dataset.n_cols; ++i) + const size_t numPoints = dataset.n_cols; + const size_t numClusters = centroids.n_cols; + + #pragma omp parallel { - const double m = std::max(minClusterDistances(assignments[i]), - lowerBounds(i)); + arma::mat localNewCentroids(centroids.n_rows, centroids.n_cols, arma::fill::zeros); + arma::Col localCounts(centroids.n_cols, arma::fill::zeros); + size_t localHamerlyPruned = 0; + size_t localDistanceCalculations = 0; - // First bound test. - if (upperBounds(i) <= m + eps) + #pragma omp for schedule(static) + for (size_t i = 0; i < numPoints; ++i) { - ++hamerlyPruned; - #ifdef MLPACK_USE_OPENMP - #pragma omp critical - #endif + const double m = std::max(minClusterDistances(assignments[i]), + lowerBounds(i)); + + // First bound test. + if (upperBounds(i) <= m + eps) { - newCentroids.col(assignments[i]) += dataset.col(i); - ++counts(assignments[i]); - } - continue; - } - - // Tighten upper bound. - upperBounds(i) = distance.Evaluate(dataset.col(i), - centroids.col(assignments[i])); - ++distanceCalculations; - - // Second bound test. - if (upperBounds(i) <= m + eps) - { - #ifdef MLPACK_USE_OPENMP - #pragma omp critical - #endif - { - newCentroids.col(assignments[i]) += dataset.col(i); - ++counts(assignments[i]); - } - continue; - } - - // The bounds failed. So test against all other clusters. - // This is Hamerly's Point-All-Ctrs() function from the paper. - // We have to reset the lower bound first. - lowerBounds(i) = DBL_MAX; - size_t newAssignment = assignments[i]; - double newUpperBound = upperBounds(i); - double newLowerBound = DBL_MAX; - - for (size_t c = 0; c < centroids.n_cols; ++c) - { - if (c == assignments[i]) + ++localHamerlyPruned; + localNewCentroids.col(assignments[i]) += dataset.col(i); + ++localCounts(assignments[i]); continue; - - const double dist = distance.Evaluate(dataset.col(i), centroids.col(c)); - - // Is this a better cluster? - if (dist < newUpperBound) - { - newLowerBound = newUpperBound; - newUpperBound = dist; - newAssignment = c; } - else if (dist < newLowerBound) + + // Tighten upper bound. + upperBounds(i) = distance.Evaluate(dataset.col(i), + centroids.col(assignments[i])); + ++localDistanceCalculations; + + // Second bound test. + if (upperBounds(i) <= m + eps) { - newLowerBound = dist; + localNewCentroids.col(assignments[i]) += dataset.col(i); + ++localCounts(assignments[i]); + continue; } + + // The bounds failed. So test against all other clusters. + lowerBounds(i) = DBL_MAX; + size_t newAssignment = assignments[i]; + double newUpperBound = upperBounds(i); + double newLowerBound = DBL_MAX; + + for (size_t c = 0; c < numClusters; ++c) + { + if (c == assignments[i]) + continue; + + const double dist = distance.Evaluate(dataset.col(i), centroids.col(c)); + + // Is this a better cluster? + if (dist < newUpperBound) + { + newLowerBound = newUpperBound; + newUpperBound = dist; + newAssignment = c; + } + else if (dist < newLowerBound) + { + newLowerBound = dist; + } + } + localDistanceCalculations += numClusters - 1; + + // Update bounds and assignment + upperBounds(i) = newUpperBound; + lowerBounds(i) = newLowerBound; + assignments[i] = newAssignment; + + // Update new centroids. + localNewCentroids.col(newAssignment) += dataset.col(i); + ++localCounts(newAssignment); } - distanceCalculations += centroids.n_cols - 1; - // Update bounds and assignment - upperBounds(i) = newUpperBound; - lowerBounds(i) = newLowerBound; - assignments[i] = newAssignment; - - // Update new centroids. - #ifdef MLPACK_USE_OPENMP - #pragma omp critical - #endif + #pragma omp critical { - newCentroids.col(newAssignment) += dataset.col(i); - ++counts(newAssignment); + newCentroids += localNewCentroids; + counts += localCounts; + hamerlyPruned += localHamerlyPruned; + distanceCalculations += localDistanceCalculations; } } @@ -167,14 +168,12 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, double furthestMovement = 0.0; double secondFurthestMovement = 0.0; size_t furthestMovingCluster = 0; - arma::vec centroidMovements(centroids.n_cols); + arma::vec centroidMovements(numClusters); double centroidMovement = 0.0; - #ifdef MLPACK_USE_OPENMP - #pragma omp parallel for reduction(+:distanceCalculations, centroidMovement) \ - reduction(max:furthestMovement) - #endif - for (size_t c = 0; c < centroids.n_cols; ++c) + #pragma omp parallel for reduction(+:centroidMovement) \ + reduction(max:furthestMovement) schedule(static) + for (size_t c = 0; c < numClusters; ++c) { if (counts(c) > 0) newCentroids.col(c) /= counts(c); @@ -185,13 +184,10 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, const double movement = std::sqrt(arma::sum(arma::square(centroids.col(c) - newCentroids.col(c)))); centroidMovements(c) = movement; centroidMovement += std::pow(movement, 2.0); - ++distanceCalculations; if (movement > furthestMovement) { - #ifdef MLPACK_USE_OPENMP - #pragma omp critical - #endif + #pragma omp critical { if (movement > furthestMovement) { @@ -208,12 +204,10 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, } // Now update bounds - #ifdef MLPACK_USE_OPENMP - #pragma omp parallel for - #endif - for (size_t i = 0; i < dataset.n_cols; ++i) + #pragma omp parallel for schedule(static) + for (size_t i = 0; i < numPoints; ++i) { - if (assignments[i] < centroids.n_cols) + if (assignments[i] < numClusters) { upperBounds(i) += centroidMovements(assignments[i]); if (assignments[i] == furthestMovingCluster) @@ -223,10 +217,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, } else { - // Handle invalid assignment - #ifdef MLPACK_USE_OPENMP - #pragma omp critical - #endif + #pragma omp critical { Log::Warn << "Invalid assignment for point " << i << std::endl; } From e60dc3807ac7e882ff24c5b95ff29b8977bda321 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Mon, 29 Jul 2024 16:41:35 +0100 Subject: [PATCH 179/212] fixed bug where dims would get zeroed out --- src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index 1523ec433d..214fddd58e 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -153,7 +153,7 @@ void NearestInterpolationType::ComputeOutputDimensions() throw std::runtime_error("Insufficient number of input dimensions."); } this->outputDimensions = this->inputDimensions; - for (size_t i = 0; i < this->InputDimensions().size() - 1; i++) + for (size_t i = 0; i < scaleFactors.size(); i++) { this->outputDimensions[i] = std::round( (double)this->outputDimensions[i] * scaleFactors[i]); From 526201a211a0a128b2484f2cae0795b5b14ae2a6 Mon Sep 17 00:00:00 2001 From: Andrew Furey Date: Mon, 29 Jul 2024 16:44:33 +0100 Subject: [PATCH 180/212] better exception message --- src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp index 214fddd58e..bb2ec09ec4 100644 --- a/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp +++ b/src/mlpack/methods/ann/layer/nearest_interpolation_impl.hpp @@ -150,7 +150,11 @@ template void NearestInterpolationType::ComputeOutputDimensions() { if (this->inputDimensions.size() < scaleFactors.size()) { - throw std::runtime_error("Insufficient number of input dimensions."); + std::ostringstream oss; + oss << "NearestInterpolation::ComputeOutputDimensions(): input dimensions " + << "must be at least 2 (received input with " << this->inputDimensions.size() + << " dimensions)!"; + throw std::runtime_error(oss.str()); } this->outputDimensions = this->inputDimensions; for (size_t i = 0; i < scaleFactors.size(); i++) From 02c293f3106ee9f0c2325ec416e8adaef9708daf Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Tue, 30 Jul 2024 17:35:55 +0200 Subject: [PATCH 181/212] Simplify OpenMP, improve distance calc, reduce memory --- .../methods/kmeans/naive_kmeans_impl.hpp | 94 +++++++++---------- 1 file changed, 42 insertions(+), 52 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 940998dd3c..e9be6a6405 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -41,82 +41,72 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, newCentroids.zeros(dims, clusters); counts.zeros(clusters); - // Pre-compute centroid norms if using Euclidean distance - arma::vec centroidNorms; - if (std::is_same::value) - { - centroidNorms = arma::sum(arma::square(centroids), 0).t(); - } + // Determine the number of threads + size_t numThreads = 1; + #ifdef MLPACK_USE_OPENMP + numThreads = static_cast(omp_get_max_threads()); + #endif - #pragma omp parallel + // Pre-allocate thread-local storage + std::vector threadCentroids(numThreads, arma::mat(dims, clusters)); + std::vector> threadCounts(numThreads, arma::Col(clusters)); + + double cNorm = 0.0; + + #pragma omp parallel reduction(+:cNorm) { - // Thread-local storage - arma::mat localNewCentroids(dims, clusters, arma::fill::zeros); - arma::Col localCounts(clusters, arma::fill::zeros); + const size_t threadId = + #ifdef MLPACK_USE_OPENMP + omp_get_thread_num(); + #else + 0; + #endif + + arma::mat& localCentroids = threadCentroids[threadId]; + arma::Col& localCounts = threadCounts[threadId]; #pragma omp for schedule(static) for (size_t i = 0; i < points; ++i) { size_t closestCluster = 0; double minDistance = std::numeric_limits::max(); - const auto& point = dataset.col(i); + const arma::vec& point = dataset.col(i); - if (std::is_same::value) + for (size_t j = 0; j < clusters; ++j) { - // Optimized Euclidean distance calculation - const double pointNorm = arma::dot(point, point); - for (size_t j = 0; j < clusters; ++j) + const double dist = distance.Evaluate(point, centroids.col(j)); + if (dist < minDistance) { - const double dist = pointNorm + centroidNorms[j] - 2 * arma::dot(point, centroids.col(j)); - if (dist < minDistance) - { - minDistance = dist; - closestCluster = j; - } - } - } - else - { - // General distance metric - for (size_t j = 0; j < clusters; ++j) - { - const double dist = distance.Evaluate(point, centroids.col(j)); - if (dist < minDistance) - { - minDistance = dist; - closestCluster = j; - } + minDistance = dist; + closestCluster = j; } } - // Update local centroids and counts - localNewCentroids.col(closestCluster) += point; + localCentroids.col(closestCluster) += point; localCounts[closestCluster]++; } - - // Combine results - #pragma omp critical - { - newCentroids += localNewCentroids; - counts += localCounts; - } } - // Normalize the centroids - for (size_t j = 0; j < clusters; ++j) + // Combine results + for (size_t t = 0; t < numThreads; ++t) { - if (counts[j] > 0) - newCentroids.col(j) /= counts[j]; - else - newCentroids.col(j) = centroids.col(j); + newCentroids += threadCentroids[t]; + counts += threadCounts[t]; } - // Calculate cluster distortion - double cNorm = 0.0; + // Normalize the centroids and calculate distortion #pragma omp parallel for reduction(+:cNorm) schedule(static) for (size_t j = 0; j < clusters; ++j) { - cNorm += std::pow(arma::norm(centroids.col(j) - newCentroids.col(j)), 2); + if (counts[j] > 0) + { + newCentroids.col(j) /= counts[j]; + cNorm += std::pow(distance.Evaluate(centroids.col(j), newCentroids.col(j)), 2.0); + } + else + { + newCentroids.col(j) = centroids.col(j); + } } distanceCalculations += clusters * points; From 86052daeb475af5eab2488fdbc5dc6b39bb4ceaa Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Fri, 2 Aug 2024 12:15:08 +0200 Subject: [PATCH 182/212] fix --- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index e9be6a6405..63a11ec97a 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -48,8 +48,8 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, #endif // Pre-allocate thread-local storage - std::vector threadCentroids(numThreads, arma::mat(dims, clusters)); - std::vector> threadCounts(numThreads, arma::Col(clusters)); + std::vector threadCentroids(numThreads, arma::mat(dims, clusters, arma::fill::zeros)); + std::vector> threadCounts(numThreads, arma::Col(clusters, arma::fill::zeros)); double cNorm = 0.0; @@ -70,7 +70,12 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, { size_t closestCluster = 0; double minDistance = std::numeric_limits::max(); - const arma::vec& point = dataset.col(i); + + arma::vec point(dims); + for (typename MatType::const_col_iterator it = dataset.begin_col(i); it != dataset.end_col(i); ++it) + { + point(it.row()) = *it; + } for (size_t j = 0; j < clusters; ++j) { From e5b8d5fbd5830e2c2b7b29f74613253a26465add Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Tue, 6 Aug 2024 19:30:44 +0200 Subject: [PATCH 183/212] fix --- .../methods/kmeans/hamerly_kmeans_impl.hpp | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index 7a984549ca..7e8323b32c 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -172,7 +172,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, double centroidMovement = 0.0; #pragma omp parallel for reduction(+:centroidMovement) \ - reduction(max:furthestMovement) schedule(static) + reduction(max:furthestMovement, secondFurthestMovement) schedule(static) for (size_t c = 0; c < numClusters; ++c) { if (counts(c) > 0) @@ -187,19 +187,13 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, if (movement > furthestMovement) { - #pragma omp critical - { - if (movement > furthestMovement) - { - secondFurthestMovement = furthestMovement; - furthestMovement = movement; - furthestMovingCluster = c; - } - else if (movement > secondFurthestMovement) - { - secondFurthestMovement = movement; - } - } + secondFurthestMovement = furthestMovement; + furthestMovement = movement; + furthestMovingCluster = c; + } + else if (movement > secondFurthestMovement) + { + secondFurthestMovement = movement; } } From 49e72835a888605f100c513d667d13ee58ac44aa Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Thu, 8 Aug 2024 17:20:26 +0200 Subject: [PATCH 184/212] fix --- .../methods/kmeans/naive_kmeans_impl.hpp | 104 +++++++++--------- 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 63a11ec97a..5069fc351a 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -29,57 +29,60 @@ NaiveKMeans::NaiveKMeans(const MatType& dataset, distanceCalculations(0) { /* Nothing to do. */ } +// Run a single iteration. template double NaiveKMeans::Iterate(const arma::mat& centroids, arma::mat& newCentroids, arma::Col& counts) { - const size_t dims = dataset.n_rows; - const size_t points = dataset.n_cols; - const size_t clusters = centroids.n_cols; + newCentroids.zeros(centroids.n_rows, centroids.n_cols); + counts.zeros(centroids.n_cols); - newCentroids.zeros(dims, clusters); - counts.zeros(clusters); - - // Determine the number of threads + // Determine the number of threads and calculate segment size size_t numThreads = 1; #ifdef MLPACK_USE_OPENMP - numThreads = static_cast(omp_get_max_threads()); + numThreads = omp_get_max_threads(); #endif + const size_t points = dataset.n_cols; + const size_t nominalSegmentSize = (points + numThreads - 1) / numThreads; // Ceiling division + // Pre-allocate thread-local storage - std::vector threadCentroids(numThreads, arma::mat(dims, clusters, arma::fill::zeros)); - std::vector> threadCounts(numThreads, arma::Col(clusters, arma::fill::zeros)); + std::vector threadCentroids(numThreads, arma::mat(centroids.n_rows, centroids.n_cols)); + std::vector> threadCounts(numThreads, arma::Col(centroids.n_cols)); - double cNorm = 0.0; - - #pragma omp parallel reduction(+:cNorm) + // Precompute squared norms of centroids + arma::vec centroidNorms(centroids.n_cols); + #pragma omp parallel for + for (size_t j = 0; j < centroids.n_cols; ++j) { - const size_t threadId = + centroidNorms(j) = arma::dot(centroids.col(j), centroids.col(j)); + } + + #pragma omp parallel + { + size_t threadId = 0; #ifdef MLPACK_USE_OPENMP - omp_get_thread_num(); - #else - 0; + threadId = omp_get_thread_num(); #endif + const size_t segmentStart = threadId * nominalSegmentSize; + const size_t segmentEnd = std::min(segmentStart + nominalSegmentSize, points); + arma::mat& localCentroids = threadCentroids[threadId]; arma::Col& localCounts = threadCounts[threadId]; - #pragma omp for schedule(static) - for (size_t i = 0; i < points; ++i) + for (size_t i = segmentStart; i < segmentEnd; ++i) { - size_t closestCluster = 0; - double minDistance = std::numeric_limits::max(); + const arma::vec& dataPoint = dataset.col(i); + double dataNorm = arma::dot(dataPoint, dataPoint); - arma::vec point(dims); - for (typename MatType::const_col_iterator it = dataset.begin_col(i); it != dataset.end_col(i); ++it) - { - point(it.row()) = *it; - } + double minDistance = std::numeric_limits::infinity(); + size_t closestCluster = centroids.n_cols; // Invalid value. - for (size_t j = 0; j < clusters; ++j) + for (size_t j = 0; j < centroids.n_cols; ++j) { - const double dist = distance.Evaluate(point, centroids.col(j)); + const double dist = std::max(0.0, dataNorm + centroidNorms(j) - 2 * arma::dot(dataPoint, centroids.col(j))); if (dist < minDistance) { minDistance = dist; @@ -87,34 +90,35 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, } } - localCentroids.col(closestCluster) += point; - localCounts[closestCluster]++; + Log::Assert(closestCluster != centroids.n_cols); + + // We now have the minimum distance centroid index. Update that centroid. + localCentroids.unsafe_col(closestCluster) += dataPoint; + localCounts(closestCluster)++; } - } - // Combine results - for (size_t t = 0; t < numThreads; ++t) - { - newCentroids += threadCentroids[t]; - counts += threadCounts[t]; - } - - // Normalize the centroids and calculate distortion - #pragma omp parallel for reduction(+:cNorm) schedule(static) - for (size_t j = 0; j < clusters; ++j) - { - if (counts[j] > 0) + // Combine calculated state from each thread + #pragma omp critical { - newCentroids.col(j) /= counts[j]; - cNorm += std::pow(distance.Evaluate(centroids.col(j), newCentroids.col(j)), 2.0); - } - else - { - newCentroids.col(j) = centroids.col(j); + newCentroids += localCentroids; + counts += localCounts; } } - distanceCalculations += clusters * points; + // Now normalize the centroid. + for (size_t i = 0; i < centroids.n_cols; ++i) + if (counts(i) != 0) + newCentroids.col(i) /= counts(i); + + distanceCalculations += centroids.n_cols * dataset.n_cols; + + // Calculate cluster distortion for this iteration. + double cNorm = 0.0; + for (size_t i = 0; i < centroids.n_cols; ++i) + { + cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), 2.0); + } + distanceCalculations += centroids.n_cols; return std::sqrt(cNorm); } From 708226d8af65cc0d56c461251b48ee297b8ac78a Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 12 Aug 2024 15:07:31 +0200 Subject: [PATCH 185/212] fix: handle bowth dense and sparse matrices --- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 5069fc351a..0052733e76 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -74,7 +74,13 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, for (size_t i = segmentStart; i < segmentEnd; ++i) { - const arma::vec& dataPoint = dataset.col(i); + // Use a temporary dense vector for both sparse and dense matrices + arma::vec dataPoint; + if (std::is_same>::value) + dataPoint = arma::vec(arma::conv_to::from(dataset.col(i))); + else + dataPoint = dataset.col(i); + double dataNorm = arma::dot(dataPoint, dataPoint); double minDistance = std::numeric_limits::infinity(); From f26ca1851cf5fbdc74746ad80e7b51fd4dffdb24 Mon Sep 17 00:00:00 2001 From: Dejan Bogosavljev Date: Wed, 14 Aug 2024 23:25:28 +0200 Subject: [PATCH 186/212] Update the name of the fedora package --- doc/quickstart/cpp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/quickstart/cpp.md b/doc/quickstart/cpp.md index 18c3781b79..da1fc7d133 100644 --- a/doc/quickstart/cpp.md +++ b/doc/quickstart/cpp.md @@ -24,7 +24,7 @@ sudo apt-get install libmlpack-dev and on Fedora or Red Hat: ```sh -sudo dnf install mlpack +sudo dnf install mlpack-devel ``` You can also use a Docker image from Dockerhub, From 8b152699c901e0e793934598cf3197f550f1bae3 Mon Sep 17 00:00:00 2001 From: Dejan Bogosavljev Date: Wed, 14 Aug 2024 23:58:30 +0200 Subject: [PATCH 187/212] Fix SVM wikipedia hyperlink --- doc/user/core.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/core.md b/doc/user/core.md index 22ce081c0b..0062191185 100644 --- a/doc/user/core.md +++ b/doc/user/core.md @@ -1940,7 +1940,7 @@ std::cout << "Kernel values between two floating-point vectors: " << k5 ### `HyperbolicTangentKernel` The `HyperbolicTangentKernel` implements the -[hyperbolic tangent kernel](https://en.wikipedia.org/wiki/Support_vector_machine#Nonlinear_Kernels), +[hyperbolic tangent kernel](https://en.wikipedia.org/wiki/Support_vector_machine#Nonlinear_kernels), which is defined by the following equation: `f(x1, x2) = tanh(s * (x1^T x2) + t)` where `s` is the scale parameter and `t` is the offset parameter. From f3648c402969f021bf34e1e66165bf2dbd179578 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Mon, 19 Aug 2024 19:56:13 +0200 Subject: [PATCH 188/212] removed segmentStart and segmentEnd --- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 0052733e76..c6aabc4463 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -66,13 +66,11 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, threadId = omp_get_thread_num(); #endif - const size_t segmentStart = threadId * nominalSegmentSize; - const size_t segmentEnd = std::min(segmentStart + nominalSegmentSize, points); - arma::mat& localCentroids = threadCentroids[threadId]; arma::Col& localCounts = threadCounts[threadId]; - for (size_t i = segmentStart; i < segmentEnd; ++i) + #pragma omp for + for (size_t i = 0; i < points; ++i) { // Use a temporary dense vector for both sparse and dense matrices arma::vec dataPoint; From a33e6912666a9808d2524cab2f0a7af3e586a18c Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 21 Aug 2024 00:20:08 +0200 Subject: [PATCH 189/212] optimization: static openmp --- .../methods/kmeans/naive_kmeans_impl.hpp | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index c6aabc4463..597ffe4e99 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -44,21 +44,26 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, numThreads = omp_get_max_threads(); #endif - const size_t points = dataset.n_cols; - const size_t nominalSegmentSize = (points + numThreads - 1) / numThreads; // Ceiling division - - // Pre-allocate thread-local storage std::vector threadCentroids(numThreads, arma::mat(centroids.n_rows, centroids.n_cols)); std::vector> threadCounts(numThreads, arma::Col(centroids.n_cols)); // Precompute squared norms of centroids arma::vec centroidNorms(centroids.n_cols); + arma::vec dataNorms(dataset.n_cols); + + // Precompute centroid norms and data point norms. #pragma omp parallel for for (size_t j = 0; j < centroids.n_cols; ++j) { centroidNorms(j) = arma::dot(centroids.col(j), centroids.col(j)); } + #pragma omp parallel for + for (size_t i = 0; i < dataset.n_cols; ++i) + { + dataNorms(i) = arma::dot(dataset.col(i), dataset.col(i)); + } + #pragma omp parallel { size_t threadId = 0; @@ -69,24 +74,15 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, arma::mat& localCentroids = threadCentroids[threadId]; arma::Col& localCounts = threadCounts[threadId]; - #pragma omp for - for (size_t i = 0; i < points; ++i) + #pragma omp for schedule(static) nowait + for (size_t i = 0; i < dataset.n_cols; ++i) { - // Use a temporary dense vector for both sparse and dense matrices - arma::vec dataPoint; - if (std::is_same>::value) - dataPoint = arma::vec(arma::conv_to::from(dataset.col(i))); - else - dataPoint = dataset.col(i); - - double dataNorm = arma::dot(dataPoint, dataPoint); - double minDistance = std::numeric_limits::infinity(); size_t closestCluster = centroids.n_cols; // Invalid value. for (size_t j = 0; j < centroids.n_cols; ++j) { - const double dist = std::max(0.0, dataNorm + centroidNorms(j) - 2 * arma::dot(dataPoint, centroids.col(j))); + const double dist = std::max(0.0, dataNorms(i) + centroidNorms(j) - 2 * arma::dot(dataset.col(i), centroids.col(j))); if (dist < minDistance) { minDistance = dist; @@ -96,12 +92,11 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, Log::Assert(closestCluster != centroids.n_cols); - // We now have the minimum distance centroid index. Update that centroid. - localCentroids.unsafe_col(closestCluster) += dataPoint; + localCentroids.unsafe_col(closestCluster) += dataset.col(i); localCounts(closestCluster)++; } - // Combine calculated state from each thread + // Combine results from each thread. #pragma omp critical { newCentroids += localCentroids; @@ -109,15 +104,17 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, } } - // Now normalize the centroid. for (size_t i = 0; i < centroids.n_cols; ++i) + { if (counts(i) != 0) newCentroids.col(i) /= counts(i); + } distanceCalculations += centroids.n_cols * dataset.n_cols; - // Calculate cluster distortion for this iteration. + // Calculate the cluster distortion (optional for this case). double cNorm = 0.0; + #pragma omp parallel for reduction(+:cNorm) for (size_t i = 0; i < centroids.n_cols; ++i) { cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), 2.0); From 8aafcb7ec9aff31c4cb5d9705beb39ed3cbafbb8 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 24 Aug 2024 13:12:47 -0400 Subject: [PATCH 190/212] Fix classification of mlpack_hoeffding_tree binding. --- src/mlpack/methods/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/CMakeLists.txt b/src/mlpack/methods/CMakeLists.txt index 11e926ee69..7dc1c46ad5 100644 --- a/src/mlpack/methods/CMakeLists.txt +++ b/src/mlpack/methods/CMakeLists.txt @@ -26,7 +26,7 @@ add_all_bindings(hmm hmm_train "Misc. / Other") add_all_bindings(hmm hmm_generate "Misc. / Other") add_all_bindings(hmm hmm_loglik "Misc. / Other") add_all_bindings(hmm hmm_viterbi "Misc. / Other") -add_all_bindings(hoeffding_trees hoeffding_tree "Clustering") +add_all_bindings(hoeffding_trees hoeffding_tree "Classification") add_all_bindings(kde kde "Misc. / Other") add_all_bindings(kernel_pca kernel_pca "Transformations") add_all_bindings(kmeans kmeans "Clustering") From 7161eb512892f39fea5f172994655940457a265c Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 24 Aug 2024 13:12:55 -0400 Subject: [PATCH 191/212] Add notes indicating that documentation is not yet complete. --- doc/index.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/index.md b/doc/index.md index 8fe5b875b1..34aa7fea2f 100644 --- a/doc/index.md +++ b/doc/index.md @@ -100,6 +100,11 @@ Predict continuous values. ### Clustering algorithms +***NOTE:*** this documentation is still under construction and so some +algorithms that mlpack implements are not yet listed here. For now, see +[the mlpack/methods directory](https://github.com/mlpack/mlpack/tree/master/src/mlpack/methods) +for a full list of algorithms. + Group points into clusters. * [`MeanShift`](user/methods/mean_shift.md): clustering with the density-based @@ -107,18 +112,33 @@ Group points into clusters. ### Geometric algorithms +***NOTE:*** this documentation is still under construction and so no geometric +algorithms in mlpack are documented yet. For now, see +[the mlpack/methods directory](https://github.com/mlpack/mlpack/tree/master/src/mlpack/methods) +for a full list of algorithms. + Computations based on distance metrics. ### Preprocessing utilities +***NOTE:*** this documentation is still under construction and so no +preprocessing utilities in mlpack are documented yet. For now, see +[the mlpack/methods/preprocess directory](https://github.com/mlpack/mlpack/tree/master/src/mlpack/methods) +for a full list of algorithms. + Prepare data for machine learning algorithms. ### Transformations +***NOTE:*** this documentation is still under construction and so some +algorithms that mlpack implements are not yet listed here. For now, see +[the mlpack/methods directory](https://github.com/mlpack/mlpack/tree/master/src/mlpack/methods) +for a full list of algorithms. + Transform data from one space to another. * [`AMF`](user/methods/amf.md): alternating matrix factorization From 4ac1dd8cc10738344335a523c0dda198efaae352 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 24 Aug 2024 11:18:44 -0400 Subject: [PATCH 192/212] Host locally because the other source has been hijacked. --- doc/user/methods/decision_tree_regressor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/methods/decision_tree_regressor.md b/doc/user/methods/decision_tree_regressor.md index b0a0cb8c21..68e07c6fc2 100644 --- a/doc/user/methods/decision_tree_regressor.md +++ b/doc/user/methods/decision_tree_regressor.md @@ -474,7 +474,7 @@ class CustomNumericSplit - ***Note***: `BestBinaryCategoricalSplit` should not be chosen when there are multiple classes and many categories. - ***Note***: for regression tasks, - [W. Fisher's proof of correctness](http://www.csiss.org/SPACE/workshops/2004/SAC/files/fisher.pdf) + [W. Fisher's proof of correctness](http://www.mlpack.org/files/fisher.pdf) only applies to when `FitnessFunction` is `MSEGain`; therefore, `BestBinaryCategoricalSplit` requires the use of `MSEGain`. * A custom class must take a [`FitnessFunction`](#fitnessfunction) as a From cd5aba3bd6ffedf7ed22dc73e98219f7f726852c Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Tue, 27 Aug 2024 02:10:43 +0200 Subject: [PATCH 193/212] opt: simplify and revert --- .../methods/kmeans/naive_kmeans_impl.hpp | 92 +++++++------------ 1 file changed, 35 insertions(+), 57 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 597ffe4e99..c9a8199b16 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -16,7 +16,6 @@ #ifndef MLPACK_METHODS_KMEANS_NAIVE_KMEANS_IMPL_HPP #define MLPACK_METHODS_KMEANS_NAIVE_KMEANS_IMPL_HPP -// In case it hasn't been included yet. #include "naive_kmeans.hpp" namespace mlpack { @@ -29,60 +28,40 @@ NaiveKMeans::NaiveKMeans(const MatType& dataset, distanceCalculations(0) { /* Nothing to do. */ } -// Run a single iteration. template double NaiveKMeans::Iterate(const arma::mat& centroids, arma::mat& newCentroids, arma::Col& counts) { - newCentroids.zeros(centroids.n_rows, centroids.n_cols); - counts.zeros(centroids.n_cols); + const size_t dims = centroids.n_rows; + const size_t numCentroids = centroids.n_cols; + const size_t numPoints = dataset.n_cols; - // Determine the number of threads and calculate segment size - size_t numThreads = 1; - #ifdef MLPACK_USE_OPENMP - numThreads = omp_get_max_threads(); - #endif + newCentroids.zeros(dims, numCentroids); + counts.zeros(numCentroids); - std::vector threadCentroids(numThreads, arma::mat(centroids.n_rows, centroids.n_cols)); - std::vector> threadCounts(numThreads, arma::Col(centroids.n_cols)); - - // Precompute squared norms of centroids - arma::vec centroidNorms(centroids.n_cols); - arma::vec dataNorms(dataset.n_cols); - - // Precompute centroid norms and data point norms. - #pragma omp parallel for - for (size_t j = 0; j < centroids.n_cols; ++j) - { - centroidNorms(j) = arma::dot(centroids.col(j), centroids.col(j)); - } - - #pragma omp parallel for - for (size_t i = 0; i < dataset.n_cols; ++i) - { - dataNorms(i) = arma::dot(dataset.col(i), dataset.col(i)); - } + // Pre-allocate thread-local storage + const int numThreads = omp_get_max_threads(); + std::vector threadCentroids(numThreads, arma::mat(dims, numCentroids, arma::fill::zeros)); + std::vector> threadCounts(numThreads, arma::Col(numCentroids, arma::fill::zeros)); #pragma omp parallel { - size_t threadId = 0; - #ifdef MLPACK_USE_OPENMP - threadId = omp_get_thread_num(); - #endif + const int threadId = omp_get_thread_num(); + auto& localCentroids = threadCentroids[threadId]; + auto& localCounts = threadCounts[threadId]; - arma::mat& localCentroids = threadCentroids[threadId]; - arma::Col& localCounts = threadCounts[threadId]; - - #pragma omp for schedule(static) nowait - for (size_t i = 0; i < dataset.n_cols; ++i) + #pragma omp for schedule(static) + for (size_t i = 0; i < numPoints; ++i) { - double minDistance = std::numeric_limits::infinity(); - size_t closestCluster = centroids.n_cols; // Invalid value. + double minDistance = std::numeric_limits::max(); + size_t closestCluster = numCentroids; - for (size_t j = 0; j < centroids.n_cols; ++j) + const auto& point = dataset.col(i); + + for (size_t j = 0; j < numCentroids; ++j) { - const double dist = std::max(0.0, dataNorms(i) + centroidNorms(j) - 2 * arma::dot(dataset.col(i), centroids.col(j))); + const double dist = distance.Evaluate(point, centroids.col(j)); if (dist < minDistance) { minDistance = dist; @@ -90,36 +69,35 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, } } - Log::Assert(closestCluster != centroids.n_cols); - - localCentroids.unsafe_col(closestCluster) += dataset.col(i); + localCentroids.col(closestCluster) += point; localCounts(closestCluster)++; } - - // Combine results from each thread. - #pragma omp critical - { - newCentroids += localCentroids; - counts += localCounts; - } } - for (size_t i = 0; i < centroids.n_cols; ++i) + // Combine results from all threads + for (int t = 0; t < numThreads; ++t) + { + newCentroids += threadCentroids[t]; + counts += threadCounts[t]; + } + + // Normalize the centroids + for (size_t i = 0; i < numCentroids; ++i) { if (counts(i) != 0) newCentroids.col(i) /= counts(i); } - distanceCalculations += centroids.n_cols * dataset.n_cols; + distanceCalculations += numCentroids * numPoints; - // Calculate the cluster distortion (optional for this case). + // Calculate cluster distortion double cNorm = 0.0; - #pragma omp parallel for reduction(+:cNorm) - for (size_t i = 0; i < centroids.n_cols; ++i) + #pragma omp parallel for reduction(+:cNorm) schedule(static) + for (size_t i = 0; i < numCentroids; ++i) { cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), 2.0); } - distanceCalculations += centroids.n_cols; + distanceCalculations += numCentroids; return std::sqrt(cNorm); } From 0dffe34cdf5961e2baa488fed400c6929c5c98bd Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 27 Aug 2024 12:16:46 -0400 Subject: [PATCH 194/212] Oops, fix location of PDF. --- doc/user/methods/decision_tree_regressor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/methods/decision_tree_regressor.md b/doc/user/methods/decision_tree_regressor.md index 68e07c6fc2..b630a8cff0 100644 --- a/doc/user/methods/decision_tree_regressor.md +++ b/doc/user/methods/decision_tree_regressor.md @@ -474,7 +474,7 @@ class CustomNumericSplit - ***Note***: `BestBinaryCategoricalSplit` should not be chosen when there are multiple classes and many categories. - ***Note***: for regression tasks, - [W. Fisher's proof of correctness](http://www.mlpack.org/files/fisher.pdf) + [W. Fisher's proof of correctness](http://www.mlpack.org/papers/fisher.pdf) only applies to when `FitnessFunction` is `MSEGain`; therefore, `BestBinaryCategoricalSplit` requires the use of `MSEGain`. * A custom class must take a [`FitnessFunction`](#fitnessfunction) as a From 952126a86212821c8e721c329b1ff2dbfd76a545 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 27 Aug 2024 14:58:30 -0400 Subject: [PATCH 195/212] Use HTTPS instead of HTTP. --- doc/user/methods/decision_tree_regressor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/methods/decision_tree_regressor.md b/doc/user/methods/decision_tree_regressor.md index b630a8cff0..b465a5f4f6 100644 --- a/doc/user/methods/decision_tree_regressor.md +++ b/doc/user/methods/decision_tree_regressor.md @@ -474,7 +474,7 @@ class CustomNumericSplit - ***Note***: `BestBinaryCategoricalSplit` should not be chosen when there are multiple classes and many categories. - ***Note***: for regression tasks, - [W. Fisher's proof of correctness](http://www.mlpack.org/papers/fisher.pdf) + [W. Fisher's proof of correctness](https://www.mlpack.org/papers/fisher.pdf) only applies to when `FitnessFunction` is `MSEGain`; therefore, `BestBinaryCategoricalSplit` requires the use of `MSEGain`. * A custom class must take a [`FitnessFunction`](#fitnessfunction) as a From 9d916af47f962606ea9d9b2bea65f8c2d3fbb5c3 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 28 Aug 2024 16:46:06 +0200 Subject: [PATCH 196/212] revert to openmp optimization --- .../methods/kmeans/naive_kmeans_impl.hpp | 82 ++++++++----------- 1 file changed, 33 insertions(+), 49 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index c9a8199b16..3f90119a13 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -16,6 +16,7 @@ #ifndef MLPACK_METHODS_KMEANS_NAIVE_KMEANS_IMPL_HPP #define MLPACK_METHODS_KMEANS_NAIVE_KMEANS_IMPL_HPP +// In case it hasn't been included yet. #include "naive_kmeans.hpp" namespace mlpack { @@ -28,76 +29,59 @@ NaiveKMeans::NaiveKMeans(const MatType& dataset, distanceCalculations(0) { /* Nothing to do. */ } +// Run a single iteration. template double NaiveKMeans::Iterate(const arma::mat& centroids, arma::mat& newCentroids, arma::Col& counts) { - const size_t dims = centroids.n_rows; - const size_t numCentroids = centroids.n_cols; - const size_t numPoints = dataset.n_cols; + newCentroids.zeros(centroids.n_rows, centroids.n_cols); + counts.zeros(centroids.n_cols); - newCentroids.zeros(dims, numCentroids); - counts.zeros(numCentroids); - - // Pre-allocate thread-local storage - const int numThreads = omp_get_max_threads(); - std::vector threadCentroids(numThreads, arma::mat(dims, numCentroids, arma::fill::zeros)); - std::vector> threadCounts(numThreads, arma::Col(numCentroids, arma::fill::zeros)); - - #pragma omp parallel + // Find the closest centroid to each point and update the new centroids. + #pragma omp parallel for + for (size_t i = 0; i < dataset.n_cols; ++i) { - const int threadId = omp_get_thread_num(); - auto& localCentroids = threadCentroids[threadId]; - auto& localCounts = threadCounts[threadId]; + // Find the closest centroid to this point. + double minDistance = std::numeric_limits::max(); + size_t closestCluster = centroids.n_cols; // Invalid value. - #pragma omp for schedule(static) - for (size_t i = 0; i < numPoints; ++i) + for (size_t j = 0; j < centroids.n_cols; ++j) { - double minDistance = std::numeric_limits::max(); - size_t closestCluster = numCentroids; + const double dist = distance.Evaluate(dataset.col(i), centroids.col(j)); - const auto& point = dataset.col(i); - - for (size_t j = 0; j < numCentroids; ++j) + if (dist < minDistance) { - const double dist = distance.Evaluate(point, centroids.col(j)); - if (dist < minDistance) - { - minDistance = dist; - closestCluster = j; - } + minDistance = dist; + closestCluster = j; } + } - localCentroids.col(closestCluster) += point; - localCounts(closestCluster)++; + // We now know the closest cluster. Update that cluster's new centroid and + // the counts we are keeping. + #pragma omp critical + { + newCentroids.col(closestCluster) += dataset.col(i); + counts[closestCluster]++; } } - // Combine results from all threads - for (int t = 0; t < numThreads; ++t) - { - newCentroids += threadCentroids[t]; - counts += threadCounts[t]; - } + // Now normalize the centroid. + for (size_t i = 0; i < centroids.n_cols; ++i) + if (counts[i] != 0) + newCentroids.col(i) /= counts[i]; - // Normalize the centroids - for (size_t i = 0; i < numCentroids; ++i) - { - if (counts(i) != 0) - newCentroids.col(i) /= counts(i); - } + distanceCalculations += centroids.n_cols * dataset.n_cols; - distanceCalculations += numCentroids * numPoints; - - // Calculate cluster distortion + // Calculate cluster distortion for this iteration. double cNorm = 0.0; - #pragma omp parallel for reduction(+:cNorm) schedule(static) - for (size_t i = 0; i < numCentroids; ++i) + for (size_t i = 0; i < centroids.n_cols; ++i) { - cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), 2.0); + const double dist = distance.Evaluate(centroids.col(i), newCentroids.col(i)); + cNorm += std::pow(dist, 2.0); } - distanceCalculations += numCentroids; + + distanceCalculations += centroids.n_cols; return std::sqrt(cNorm); } From f04426f8366f0d91c1079556a793920023668d00 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Wed, 28 Aug 2024 16:57:18 +0200 Subject: [PATCH 197/212] optimize openmp usage --- .../methods/kmeans/naive_kmeans_impl.hpp | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 3f90119a13..bf1f7fbd5b 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -38,50 +38,64 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, newCentroids.zeros(centroids.n_rows, centroids.n_cols); counts.zeros(centroids.n_cols); - // Find the closest centroid to each point and update the new centroids. - #pragma omp parallel for - for (size_t i = 0; i < dataset.n_cols; ++i) + const size_t dimensions = centroids.n_rows; + const size_t numClusters = centroids.n_cols; + const size_t numPoints = dataset.n_cols; + + #pragma omp parallel { - // Find the closest centroid to this point. - double minDistance = std::numeric_limits::max(); - size_t closestCluster = centroids.n_cols; // Invalid value. + // Thread-local storage for partial sums + arma::mat threadCentroids(dimensions, numClusters, arma::fill::zeros); + arma::Col threadCounts(numClusters, arma::fill::zeros); - for (size_t j = 0; j < centroids.n_cols; ++j) + #pragma omp for schedule(static) nowait + for (size_t i = 0; i < numPoints; ++i) { - const double dist = distance.Evaluate(dataset.col(i), centroids.col(j)); + size_t closestCluster = 0; + double minDistance = std::numeric_limits::max(); - if (dist < minDistance) + // Find the closest centroid + for (size_t j = 0; j < numClusters; ++j) { - minDistance = dist; - closestCluster = j; + double dist = distance.Evaluate(dataset.col(i), centroids.col(j)); + if (dist < minDistance) + { + minDistance = dist; + closestCluster = j; + } } + + // Update thread-local centroids and counts + threadCentroids.col(closestCluster) += dataset.col(i); + threadCounts(closestCluster)++; } - // We now know the closest cluster. Update that cluster's new centroid and - // the counts we are keeping. + // Reduce thread-local results to shared variables #pragma omp critical { - newCentroids.col(closestCluster) += dataset.col(i); - counts[closestCluster]++; + newCentroids += threadCentroids; + counts += threadCounts; } } - // Now normalize the centroid. - for (size_t i = 0; i < centroids.n_cols; ++i) - if (counts[i] != 0) - newCentroids.col(i) /= counts[i]; - - distanceCalculations += centroids.n_cols * dataset.n_cols; - - // Calculate cluster distortion for this iteration. - double cNorm = 0.0; - for (size_t i = 0; i < centroids.n_cols; ++i) + // Normalize centroids + #pragma omp parallel for schedule(static) + for (size_t i = 0; i < numClusters; ++i) { - const double dist = distance.Evaluate(centroids.col(i), newCentroids.col(i)); - cNorm += std::pow(dist, 2.0); + if (counts(i) > 0) + newCentroids.col(i) /= counts(i); } - distanceCalculations += centroids.n_cols; + distanceCalculations += numClusters * numPoints; + + // Calculate cluster distortion + double cNorm = 0.0; + #pragma omp parallel for reduction(+:cNorm) schedule(static) + for (size_t i = 0; i < numClusters; ++i) + { + cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), 2.0); + } + distanceCalculations += numClusters; return std::sqrt(cNorm); } From 05c95fa4df1e5852b2d77a16762ab6203776b888 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Thu, 29 Aug 2024 18:36:31 +0200 Subject: [PATCH 198/212] cleaned --- .../methods/kmeans/naive_kmeans_impl.hpp | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index bf1f7fbd5b..98e99f681a 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -38,26 +38,23 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, newCentroids.zeros(centroids.n_rows, centroids.n_cols); counts.zeros(centroids.n_cols); - const size_t dimensions = centroids.n_rows; - const size_t numClusters = centroids.n_cols; - const size_t numPoints = dataset.n_cols; - + // Find the closest centroid to each point and update the new centroids. #pragma omp parallel { // Thread-local storage for partial sums - arma::mat threadCentroids(dimensions, numClusters, arma::fill::zeros); - arma::Col threadCounts(numClusters, arma::fill::zeros); + arma::mat threadCentroids(centroids.n_rows, centroids.n_cols, arma::fill::zeros); + arma::Col threadCounts(centroids.n_cols, arma::fill::zeros); #pragma omp for schedule(static) nowait - for (size_t i = 0; i < numPoints; ++i) + for (size_t i = 0; i < dataset.n_cols; ++i) { - size_t closestCluster = 0; + // Find the closest centroid to this point. double minDistance = std::numeric_limits::max(); + size_t closestCluster = centroids.n_cols; // Invalid value. - // Find the closest centroid - for (size_t j = 0; j < numClusters; ++j) + for (size_t j = 0; j < centroids.n_cols; ++j) { - double dist = distance.Evaluate(dataset.col(i), centroids.col(j)); + const double dist = distance.Evaluate(dataset.col(i), centroids.col(j)); if (dist < minDistance) { minDistance = dist; @@ -78,24 +75,22 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, } } - // Normalize centroids + // Now normalize the centroid. #pragma omp parallel for schedule(static) - for (size_t i = 0; i < numClusters; ++i) - { - if (counts(i) > 0) + for (size_t i = 0; i < centroids.n_cols; ++i) + if (counts(i) != 0) newCentroids.col(i) /= counts(i); - } - distanceCalculations += numClusters * numPoints; + distanceCalculations += centroids.n_cols * dataset.n_cols; - // Calculate cluster distortion + // Calculate cluster distortion for this iteration. double cNorm = 0.0; #pragma omp parallel for reduction(+:cNorm) schedule(static) - for (size_t i = 0; i < numClusters; ++i) + for (size_t i = 0; i < centroids.n_cols; ++i) { cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), 2.0); } - distanceCalculations += numClusters; + distanceCalculations += centroids.n_cols; return std::sqrt(cNorm); } From 18acd48b57ae7b50f9d37df70239b52782d3cde7 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Fri, 30 Aug 2024 20:07:15 +0200 Subject: [PATCH 199/212] revert to only improve openmp --- .../methods/kmeans/naive_kmeans_impl.hpp | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 98e99f681a..601acd96bd 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -39,22 +39,24 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, counts.zeros(centroids.n_cols); // Find the closest centroid to each point and update the new centroids. + // Computed in parallel over the complete dataset #pragma omp parallel { - // Thread-local storage for partial sums - arma::mat threadCentroids(centroids.n_rows, centroids.n_cols, arma::fill::zeros); - arma::Col threadCounts(centroids.n_cols, arma::fill::zeros); + // The current state of the K-means is private for each thread + arma::mat localCentroids(centroids.n_rows, centroids.n_cols); + arma::Col localCounts(centroids.n_cols); #pragma omp for schedule(static) nowait - for (size_t i = 0; i < dataset.n_cols; ++i) + for (size_t i = 0; i < (size_t) dataset.n_cols; ++i) { // Find the closest centroid to this point. - double minDistance = std::numeric_limits::max(); + double minDistance = std::numeric_limits::infinity(); size_t closestCluster = centroids.n_cols; // Invalid value. for (size_t j = 0; j < centroids.n_cols; ++j) { - const double dist = distance.Evaluate(dataset.col(i), centroids.col(j)); + const double dist = distance.Evaluate(dataset.col(i), + centroids.col(j)); if (dist < minDistance) { minDistance = dist; @@ -62,16 +64,17 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, } } - // Update thread-local centroids and counts - threadCentroids.col(closestCluster) += dataset.col(i); - threadCounts(closestCluster)++; - } + Log::Assert(closestCluster != centroids.n_cols); - // Reduce thread-local results to shared variables + // We now have the minimum distance centroid index. Update that centroid. + localCentroids.col(closestCluster) += dataset.col(i); + localCounts(closestCluster)++; + } + // Combine calculated state from each thread #pragma omp critical { - newCentroids += threadCentroids; - counts += threadCounts; + newCentroids += localCentroids; + counts += localCounts; } } @@ -88,7 +91,8 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, #pragma omp parallel for reduction(+:cNorm) schedule(static) for (size_t i = 0; i < centroids.n_cols; ++i) { - cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), 2.0); + cNorm += std::pow(distance.Evaluate(centroids.col(i), newCentroids.col(i)), + 2.0); } distanceCalculations += centroids.n_cols; From 6b0c3047e5db8faee9af3098ecf53c54fb775dc6 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Fri, 30 Aug 2024 20:25:09 +0200 Subject: [PATCH 200/212] line fix at the end of the file --- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 601acd96bd..ab28faadc8 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -101,4 +101,4 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, } // namespace mlpack -#endif \ No newline at end of file +#endif From 41a0a67c997f10fdf336e2734e0dc6efd4a6107d Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Sat, 31 Aug 2024 01:44:48 +0200 Subject: [PATCH 201/212] remove space Co-authored-by: Ryan Curtin --- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index ab28faadc8..e848259947 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -55,7 +55,7 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, for (size_t j = 0; j < centroids.n_cols; ++j) { - const double dist = distance.Evaluate(dataset.col(i), + const double dist = distance.Evaluate(dataset.col(i), centroids.col(j)); if (dist < minDistance) { From 21d2ad80b649b7e44889998ecafd38e5df39b35c Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Sat, 31 Aug 2024 01:47:57 +0200 Subject: [PATCH 202/212] address comments: fix optimization --- .../methods/kmeans/hamerly_kmeans_impl.hpp | 176 ++++++++---------- 1 file changed, 76 insertions(+), 100 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index 7e8323b32c..eb583d1c1b 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -26,12 +26,12 @@ HamerlyKMeans::HamerlyKMeans(const MatType& dataset, { // Nothing to do. } + template double HamerlyKMeans::Iterate(const arma::mat& centroids, arma::mat& newCentroids, arma::Col& counts) { - static constexpr double eps = std::numeric_limits::epsilon(); size_t hamerlyPruned = 0; // If this is the first iteration, we need to set all the bounds. @@ -44,184 +44,160 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, minClusterDistances.set_size(centroids.n_cols); } - // Reset new centroids and counts. + // Reset new centroids. newCentroids.zeros(centroids.n_rows, centroids.n_cols); counts.zeros(centroids.n_cols); // Calculate minimum intra-cluster distance for each cluster. minClusterDistances.fill(DBL_MAX); - - #pragma omp parallel + #pragma omp parallel for reduction(+:distanceCalculations) schedule(static) + for (size_t i = 0; i < centroids.n_cols; ++i) { - arma::vec localMinClusterDistances(centroids.n_cols, arma::fill::value(DBL_MAX)); - #pragma omp for reduction(+:distanceCalculations) schedule(dynamic) - for (size_t i = 0; i < centroids.n_cols; ++i) + for (size_t j = i + 1; j < centroids.n_cols; ++j) { - for (size_t j = i + 1; j < centroids.n_cols; ++j) - { - const double dist = distance.Evaluate(centroids.col(i), centroids.col(j)); - ++distanceCalculations; + const double dist = distance.Evaluate(centroids.col(i), + centroids.col(j)) / 2.0; + ++distanceCalculations; - if (dist > eps) - { - const double halfDist = dist / 2.0; - localMinClusterDistances(i) = std::min(localMinClusterDistances(i), halfDist); - localMinClusterDistances(j) = std::min(localMinClusterDistances(j), halfDist); - } - } - } - - #pragma omp critical - { - for (size_t i = 0; i < centroids.n_cols; ++i) - { - minClusterDistances(i) = std::min(minClusterDistances(i), localMinClusterDistances(i)); - } + // Update bounds, if this intra-cluster distance is smaller. + minClusterDistances(i) = std::min(minClusterDistances(i), dist); + minClusterDistances(j) = std::min(minClusterDistances(j), dist); } } - const size_t numPoints = dataset.n_cols; - const size_t numClusters = centroids.n_cols; + arma::mat localNewCentroids(centroids.n_rows, centroids.n_cols, arma::fill::zeros); + arma::Col localCounts(centroids.n_cols, arma::fill::zeros); #pragma omp parallel { - arma::mat localNewCentroids(centroids.n_rows, centroids.n_cols, arma::fill::zeros); - arma::Col localCounts(centroids.n_cols, arma::fill::zeros); - size_t localHamerlyPruned = 0; - size_t localDistanceCalculations = 0; + arma::mat threadNewCentroids(centroids.n_rows, centroids.n_cols, arma::fill::zeros); + arma::Col threadCounts(centroids.n_cols, arma::fill::zeros); + size_t threadHamerlyPruned = 0; + size_t threadDistanceCalculations = 0; #pragma omp for schedule(static) - for (size_t i = 0; i < numPoints; ++i) + for (size_t i = 0; i < dataset.n_cols; ++i) { const double m = std::max(minClusterDistances(assignments[i]), lowerBounds(i)); // First bound test. - if (upperBounds(i) <= m + eps) + if (upperBounds(i) <= m) { - ++localHamerlyPruned; - localNewCentroids.col(assignments[i]) += dataset.col(i); - ++localCounts(assignments[i]); + ++threadHamerlyPruned; + threadNewCentroids.col(assignments[i]) += dataset.col(i); + ++threadCounts(assignments[i]); continue; } // Tighten upper bound. upperBounds(i) = distance.Evaluate(dataset.col(i), centroids.col(assignments[i])); - ++localDistanceCalculations; + ++threadDistanceCalculations; // Second bound test. - if (upperBounds(i) <= m + eps) + if (upperBounds(i) <= m) { - localNewCentroids.col(assignments[i]) += dataset.col(i); - ++localCounts(assignments[i]); + threadNewCentroids.col(assignments[i]) += dataset.col(i); + ++threadCounts(assignments[i]); continue; } - // The bounds failed. So test against all other clusters. + // The bounds failed. So test against all other clusters. + // This is Hamerly's Point-All-Ctrs() function from the paper. + // We have to reset the lower bound first. lowerBounds(i) = DBL_MAX; - size_t newAssignment = assignments[i]; - double newUpperBound = upperBounds(i); - double newLowerBound = DBL_MAX; - - for (size_t c = 0; c < numClusters; ++c) + for (size_t c = 0; c < centroids.n_cols; ++c) { if (c == assignments[i]) continue; const double dist = distance.Evaluate(dataset.col(i), centroids.col(c)); - // Is this a better cluster? - if (dist < newUpperBound) + // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)). + if (dist < upperBounds(i)) { - newLowerBound = newUpperBound; - newUpperBound = dist; - newAssignment = c; + // lowerBounds holds the second closest cluster. + lowerBounds(i) = upperBounds(i); + upperBounds(i) = dist; + assignments[i] = c; } - else if (dist < newLowerBound) + else if (dist < lowerBounds(i)) { - newLowerBound = dist; + // This is a closer second-closest cluster. + lowerBounds(i) = dist; } } - localDistanceCalculations += numClusters - 1; - - // Update bounds and assignment - upperBounds(i) = newUpperBound; - lowerBounds(i) = newLowerBound; - assignments[i] = newAssignment; + threadDistanceCalculations += centroids.n_cols - 1; // Update new centroids. - localNewCentroids.col(newAssignment) += dataset.col(i); - ++localCounts(newAssignment); + threadNewCentroids.col(assignments[i]) += dataset.col(i); + ++threadCounts(assignments[i]); } #pragma omp critical { - newCentroids += localNewCentroids; - counts += localCounts; - hamerlyPruned += localHamerlyPruned; - distanceCalculations += localDistanceCalculations; + localNewCentroids += threadNewCentroids; + localCounts += threadCounts; + hamerlyPruned += threadHamerlyPruned; + distanceCalculations += threadDistanceCalculations; } } - // Normalize centroids and calculate cluster movement + newCentroids = std::move(localNewCentroids); + counts = std::move(localCounts); + + // Normalize centroids and calculate cluster movement (contains parts of + // Move-Centers() and Update-Bounds()). double furthestMovement = 0.0; double secondFurthestMovement = 0.0; size_t furthestMovingCluster = 0; - arma::vec centroidMovements(numClusters); + arma::vec centroidMovements(centroids.n_cols); double centroidMovement = 0.0; - - #pragma omp parallel for reduction(+:centroidMovement) \ - reduction(max:furthestMovement, secondFurthestMovement) schedule(static) - for (size_t c = 0; c < numClusters; ++c) + #pragma omp parallel for reduction(+:distanceCalculations,centroidMovement) schedule(static) + for (size_t c = 0; c < centroids.n_cols; ++c) { if (counts(c) > 0) newCentroids.col(c) /= counts(c); - else - newCentroids.col(c) = centroids.col(c); // Calculate movement. - const double movement = std::sqrt(arma::sum(arma::square(centroids.col(c) - newCentroids.col(c)))); + const double movement = distance.Evaluate(centroids.col(c), + newCentroids.col(c)); centroidMovements(c) = movement; centroidMovement += std::pow(movement, 2.0); + ++distanceCalculations; - if (movement > furthestMovement) + #pragma omp critical { - secondFurthestMovement = furthestMovement; - furthestMovement = movement; - furthestMovingCluster = c; - } - else if (movement > secondFurthestMovement) - { - secondFurthestMovement = movement; + if (movement > furthestMovement) + { + secondFurthestMovement = furthestMovement; + furthestMovement = movement; + furthestMovingCluster = c; + } + else if (movement > secondFurthestMovement) + { + secondFurthestMovement = movement; + } } } - // Now update bounds + // Now update bounds (lines 3-8 of Update-Bounds()). #pragma omp parallel for schedule(static) - for (size_t i = 0; i < numPoints; ++i) + for (size_t i = 0; i < dataset.n_cols; ++i) { - if (assignments[i] < numClusters) - { - upperBounds(i) += centroidMovements(assignments[i]); - if (assignments[i] == furthestMovingCluster) - lowerBounds(i) -= secondFurthestMovement; - else - lowerBounds(i) -= furthestMovement; - } + upperBounds(i) += centroidMovements(assignments[i]); + if (assignments[i] == furthestMovingCluster) + lowerBounds(i) -= secondFurthestMovement; else - { - #pragma omp critical - { - Log::Warn << "Invalid assignment for point " << i << std::endl; - } - } + lowerBounds(i) -= furthestMovement; } Log::Info << "Hamerly prunes: " << hamerlyPruned << ".\n"; return std::sqrt(centroidMovement); } + } // namespace mlpack -#endif \ No newline at end of file +#endif From e168cc2267b35f6b5c47a908fb07ac17c6120c5d Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Sat, 31 Aug 2024 02:03:37 +0200 Subject: [PATCH 203/212] simplified localNewCentroids --- .../methods/kmeans/hamerly_kmeans_impl.hpp | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index eb583d1c1b..440f8da68d 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -65,9 +65,6 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, } } - arma::mat localNewCentroids(centroids.n_rows, centroids.n_cols, arma::fill::zeros); - arma::Col localCounts(centroids.n_cols, arma::fill::zeros); - #pragma omp parallel { arma::mat threadNewCentroids(centroids.n_rows, centroids.n_cols, arma::fill::zeros); @@ -103,9 +100,9 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, continue; } - // The bounds failed. So test against all other clusters. - // This is Hamerly's Point-All-Ctrs() function from the paper. - // We have to reset the lower bound first. + // The bounds failed. So test against all other clusters. + // This is Hamerly's Point-All-Ctrs() function from the paper. + // We have to reset the lower bound first. lowerBounds(i) = DBL_MAX; for (size_t c = 0; c < centroids.n_cols; ++c) { @@ -114,17 +111,17 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, const double dist = distance.Evaluate(dataset.col(i), centroids.col(c)); - // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)). + // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)). if (dist < upperBounds(i)) { - // lowerBounds holds the second closest cluster. + // lowerBounds holds the second closest cluster. lowerBounds(i) = upperBounds(i); upperBounds(i) = dist; assignments[i] = c; } else if (dist < lowerBounds(i)) { - // This is a closer second-closest cluster. + // This is a closer second-closest cluster. lowerBounds(i) = dist; } } @@ -137,16 +134,13 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, #pragma omp critical { - localNewCentroids += threadNewCentroids; - localCounts += threadCounts; + newCentroids += threadNewCentroids; + counts += threadCounts; hamerlyPruned += threadHamerlyPruned; distanceCalculations += threadDistanceCalculations; } } - newCentroids = std::move(localNewCentroids); - counts = std::move(localCounts); - // Normalize centroids and calculate cluster movement (contains parts of // Move-Centers() and Update-Bounds()). double furthestMovement = 0.0; From 009b44d1ace601a1e2f33719fd62db15f5c10317 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Sat, 31 Aug 2024 02:26:57 +0200 Subject: [PATCH 204/212] line length --- src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index 440f8da68d..ad8243fd1a 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -67,7 +67,8 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, #pragma omp parallel { - arma::mat threadNewCentroids(centroids.n_rows, centroids.n_cols, arma::fill::zeros); + arma::mat threadNewCentroids(centroids.n_rows, centroids.n_cols, + arma::fill::zeros); arma::Col threadCounts(centroids.n_cols, arma::fill::zeros); size_t threadHamerlyPruned = 0; size_t threadDistanceCalculations = 0; @@ -111,7 +112,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, const double dist = distance.Evaluate(dataset.col(i), centroids.col(c)); - // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)). + // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)) if (dist < upperBounds(i)) { // lowerBounds holds the second closest cluster. @@ -148,7 +149,8 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, size_t furthestMovingCluster = 0; arma::vec centroidMovements(centroids.n_cols); double centroidMovement = 0.0; - #pragma omp parallel for reduction(+:distanceCalculations,centroidMovement) schedule(static) + #pragma omp parallel for reduction(+:distanceCalculations,centroidMovement) \ + schedule(static) for (size_t c = 0; c < centroids.n_cols; ++c) { if (counts(c) > 0) From cf1bd082aa38db0116926df0b647a6319336f5d6 Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Sat, 31 Aug 2024 21:57:35 +0200 Subject: [PATCH 205/212] openMP custom reductions and typo fixes --- .../methods/kmeans/hamerly_kmeans_impl.hpp | 136 +++++++++--------- 1 file changed, 66 insertions(+), 70 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index ad8243fd1a..b242e15cc7 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -17,6 +17,14 @@ namespace mlpack { +// Custom reduction for arma::mat +#pragma omp declare reduction(matAdd : arma::mat : omp_out += omp_in) \ + initializer(omp_priv = arma::mat(omp_orig.n_rows, omp_orig.n_cols).zeros()) + +// Custom reduction for arma::Col +#pragma omp declare reduction(colAdd : arma::Col : omp_out += omp_in) \ + initializer(omp_priv = arma::Col(omp_orig.n_elem).zeros()) + template HamerlyKMeans::HamerlyKMeans(const MatType& dataset, DistanceType& distance) : @@ -65,83 +73,71 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, } } - #pragma omp parallel + size_t threadDistanceCalculations = 0; + + #pragma omp parallel for reduction(+:hamerlyPruned,threadDistanceCalculations) \ + reduction(matAdd:newCentroids) reduction(colAdd:counts) schedule(static) + for (size_t i = 0; i < dataset.n_cols; ++i) { - arma::mat threadNewCentroids(centroids.n_rows, centroids.n_cols, - arma::fill::zeros); - arma::Col threadCounts(centroids.n_cols, arma::fill::zeros); - size_t threadHamerlyPruned = 0; - size_t threadDistanceCalculations = 0; + const double m = std::max(minClusterDistances(assignments[i]), + lowerBounds(i)); - #pragma omp for schedule(static) - for (size_t i = 0; i < dataset.n_cols; ++i) + // First bound test. + if (upperBounds(i) <= m) { - const double m = std::max(minClusterDistances(assignments[i]), - lowerBounds(i)); - - // First bound test. - if (upperBounds(i) <= m) - { - ++threadHamerlyPruned; - threadNewCentroids.col(assignments[i]) += dataset.col(i); - ++threadCounts(assignments[i]); - continue; - } - - // Tighten upper bound. - upperBounds(i) = distance.Evaluate(dataset.col(i), - centroids.col(assignments[i])); - ++threadDistanceCalculations; - - // Second bound test. - if (upperBounds(i) <= m) - { - threadNewCentroids.col(assignments[i]) += dataset.col(i); - ++threadCounts(assignments[i]); - continue; - } - - // The bounds failed. So test against all other clusters. - // This is Hamerly's Point-All-Ctrs() function from the paper. - // We have to reset the lower bound first. - lowerBounds(i) = DBL_MAX; - for (size_t c = 0; c < centroids.n_cols; ++c) - { - if (c == assignments[i]) - continue; - - const double dist = distance.Evaluate(dataset.col(i), centroids.col(c)); - - // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)) - if (dist < upperBounds(i)) - { - // lowerBounds holds the second closest cluster. - lowerBounds(i) = upperBounds(i); - upperBounds(i) = dist; - assignments[i] = c; - } - else if (dist < lowerBounds(i)) - { - // This is a closer second-closest cluster. - lowerBounds(i) = dist; - } - } - threadDistanceCalculations += centroids.n_cols - 1; - - // Update new centroids. - threadNewCentroids.col(assignments[i]) += dataset.col(i); - ++threadCounts(assignments[i]); + ++hamerlyPruned; + newCentroids.col(assignments[i]) += dataset.col(i); + ++counts(assignments[i]); + continue; } - #pragma omp critical + // Tighten upper bound. + upperBounds(i) = distance.Evaluate(dataset.col(i), + centroids.col(assignments[i])); + ++threadDistanceCalculations; + + // Second bound test. + if (upperBounds(i) <= m) { - newCentroids += threadNewCentroids; - counts += threadCounts; - hamerlyPruned += threadHamerlyPruned; - distanceCalculations += threadDistanceCalculations; + newCentroids.col(assignments[i]) += dataset.col(i); + ++counts(assignments[i]); + continue; } + + // The bounds failed. So test against all other clusters. + // This is Hamerly's Point-All-Ctrs() function from the paper. + // We have to reset the lower bound first. + lowerBounds(i) = DBL_MAX; + for (size_t c = 0; c < centroids.n_cols; ++c) + { + if (c == assignments[i]) + continue; + + const double dist = distance.Evaluate(dataset.col(i), centroids.col(c)); + + // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)) + if (dist < upperBounds(i)) + { + // lowerBounds holds the second closest cluster. + lowerBounds(i) = upperBounds(i); + upperBounds(i) = dist; + assignments[i] = c; + } + else if (dist < lowerBounds(i)) + { + // This is a closer second-closest cluster. + lowerBounds(i) = dist; + } + } + threadDistanceCalculations += centroids.n_cols - 1; + + // Update new centroids. + newCentroids.col(assignments[i]) += dataset.col(i); + ++counts(assignments[i]); } + distanceCalculations += threadDistanceCalculations; + // Normalize centroids and calculate cluster movement (contains parts of // Move-Centers() and Update-Bounds()). double furthestMovement = 0.0; @@ -149,8 +145,8 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, size_t furthestMovingCluster = 0; arma::vec centroidMovements(centroids.n_cols); double centroidMovement = 0.0; - #pragma omp parallel for reduction(+:distanceCalculations,centroidMovement) \ - schedule(static) + #pragma omp parallel for reduction(+: distanceCalculations, centroidMovement) \ + schedule(static) for (size_t c = 0; c < centroids.n_cols; ++c) { if (counts(c) > 0) From dfac6cdd02332dd578c82e91bf1e6d8b07adf0bd Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Sun, 1 Sep 2024 17:07:59 +0200 Subject: [PATCH 206/212] updated history.md --- HISTORY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 05062d0867..ceedad33f7 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,10 @@ _????-??-??_ * Distribute STB headers as part of R package (#3724, #3726). + * Improved performance of k-means implementations using OpenMP: + - Added OpenMP parallelization to Hamerly k-means (#3761). + - Added OpenMP parallelization to Naive k-means (#3762). + - Added OpenMP parallelization to Elkan k-means (#3764). ## mlpack 4.4.0 From ca5ffb60447f88304d865f01d0e8f75660624ede Mon Sep 17 00:00:00 2001 From: Mark Fischinger Date: Sun, 1 Sep 2024 17:14:56 +0200 Subject: [PATCH 207/212] created new omp custom reduction --- src/mlpack/base.hpp | 1 + src/mlpack/core/util/omp_reductions.hpp | 27 +++++++++++++++++++ .../methods/kmeans/hamerly_kmeans_impl.hpp | 18 +++---------- 3 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 src/mlpack/core/util/omp_reductions.hpp diff --git a/src/mlpack/base.hpp b/src/mlpack/base.hpp index 780435d4c5..c296733d27 100644 --- a/src/mlpack/base.hpp +++ b/src/mlpack/base.hpp @@ -84,6 +84,7 @@ // Now include Armadillo and traits that we use for it. #include #include +#include // On Visual Studio, disable C4519 (default arguments for function templates) // since it's by default an error, which doesn't even make any sense because diff --git a/src/mlpack/core/util/omp_reductions.hpp b/src/mlpack/core/util/omp_reductions.hpp new file mode 100644 index 0000000000..1b1e979b0f --- /dev/null +++ b/src/mlpack/core/util/omp_reductions.hpp @@ -0,0 +1,27 @@ +/** + * @file core/util/omp_reductions.hpp + * @author Mark Fischinger + * + * Custom OpenMP reductions. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_UTIL_OMP_REDUCTIONS_HPP +#define MLPACK_CORE_UTIL_OMP_REDUCTIONS_HPP + +namespace mlpack { + +// Custom reduction for arma::mat +#pragma omp declare reduction(matAdd : arma::mat : omp_out += omp_in) \ + initializer(omp_priv = arma::mat(omp_orig.n_rows, omp_orig.n_cols)) + +// Custom reduction for arma::Col +#pragma omp declare reduction(colAdd : arma::Col : omp_out += omp_in) \ + initializer(omp_priv = arma::Col(omp_orig.n_elem)) + +} // namespace mlpack + +#endif \ No newline at end of file diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index b242e15cc7..3a23d1372e 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -17,14 +17,6 @@ namespace mlpack { -// Custom reduction for arma::mat -#pragma omp declare reduction(matAdd : arma::mat : omp_out += omp_in) \ - initializer(omp_priv = arma::mat(omp_orig.n_rows, omp_orig.n_cols).zeros()) - -// Custom reduction for arma::Col -#pragma omp declare reduction(colAdd : arma::Col : omp_out += omp_in) \ - initializer(omp_priv = arma::Col(omp_orig.n_elem).zeros()) - template HamerlyKMeans::HamerlyKMeans(const MatType& dataset, DistanceType& distance) : @@ -75,7 +67,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, size_t threadDistanceCalculations = 0; - #pragma omp parallel for reduction(+:hamerlyPruned,threadDistanceCalculations) \ + #pragma omp parallel for reduction(+:hamerlyPruned,distanceCalculations) \ reduction(matAdd:newCentroids) reduction(colAdd:counts) schedule(static) for (size_t i = 0; i < dataset.n_cols; ++i) { @@ -93,8 +85,8 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // Tighten upper bound. upperBounds(i) = distance.Evaluate(dataset.col(i), - centroids.col(assignments[i])); - ++threadDistanceCalculations; + centroids.col(assignments[i])); + ++distanceCalculations; // Second bound test. if (upperBounds(i) <= m) @@ -129,15 +121,13 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, lowerBounds(i) = dist; } } - threadDistanceCalculations += centroids.n_cols - 1; + distanceCalculations += centroids.n_cols - 1; // Update new centroids. newCentroids.col(assignments[i]) += dataset.col(i); ++counts(assignments[i]); } - distanceCalculations += threadDistanceCalculations; - // Normalize centroids and calculate cluster movement (contains parts of // Move-Centers() and Update-Bounds()). double furthestMovement = 0.0; From 1a2fba23a80c3fa52dddf9b9539bdbd9fcaf3ae6 Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:45:12 +0200 Subject: [PATCH 208/212] Update src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp Co-authored-by: Ryan Curtin --- src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index 3a23d1372e..f8913c9b9a 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -65,8 +65,6 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, } } - size_t threadDistanceCalculations = 0; - #pragma omp parallel for reduction(+:hamerlyPruned,distanceCalculations) \ reduction(matAdd:newCentroids) reduction(colAdd:counts) schedule(static) for (size_t i = 0; i < dataset.n_cols; ++i) From 554c25a5831d59224522a4db73e3f8e806a649c6 Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:45:21 +0200 Subject: [PATCH 209/212] Update src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp Co-authored-by: Ryan Curtin --- src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index f8913c9b9a..586919c346 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -83,7 +83,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, // Tighten upper bound. upperBounds(i) = distance.Evaluate(dataset.col(i), - centroids.col(assignments[i])); + centroids.col(assignments[i])); ++distanceCalculations; // Second bound test. From 977ed5b5d24ef897310794491fadbc9a7660ee27 Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:45:30 +0200 Subject: [PATCH 210/212] Update src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp Co-authored-by: Ryan Curtin --- src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index 586919c346..0aa03562b7 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -105,7 +105,7 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, const double dist = distance.Evaluate(dataset.col(i), centroids.col(c)); - // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)) + // Is this a better cluster? At this point, upperBounds[i] = d(i, c(i)). if (dist < upperBounds(i)) { // lowerBounds holds the second closest cluster. From 9d9526ceb9137597e3760a5d7aace7e48be7d109 Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:46:27 +0200 Subject: [PATCH 211/212] Update omp_reductions.hpp --- src/mlpack/core/util/omp_reductions.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/core/util/omp_reductions.hpp b/src/mlpack/core/util/omp_reductions.hpp index 1b1e979b0f..1a55028c20 100644 --- a/src/mlpack/core/util/omp_reductions.hpp +++ b/src/mlpack/core/util/omp_reductions.hpp @@ -24,4 +24,4 @@ namespace mlpack { } // namespace mlpack -#endif \ No newline at end of file +#endif From 65acf073fc243d85584e9703d5a80a76065547a0 Mon Sep 17 00:00:00 2001 From: Mark Fischinger <64029109+MarkFischinger@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:47:19 +0200 Subject: [PATCH 212/212] Update HISTORY.md --- HISTORY.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index eb415e335f..3f6ef2d629 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,11 +6,7 @@ _????-??-??_ * Distribute STB headers as part of R package (#3724, #3726). - * Added OpenMP parallelization to Hamerly k-means (#3761). - - * Added OpenMP parallelization to Naive k-means (#3762). - - * Added OpenMP parallelization to Elkan k-means (#3764). + * Added OpenMP parallelization to Hamerly, Naive, and Elkan k-means (#3761, #3762, #3764). * Added OpenMP support for fast approximation (#3685).