From 1cc19a7b19c447e9a39bf2109aaf65701f318f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Montan=CC=83ana?= Date: Fri, 20 Dec 2024 19:10:17 +0100 Subject: [PATCH] Refactor mpi classes --- src/CMakeLists.txt | 2 +- src/grid/GridBase.h | 12 +- src/grid/GridConfig.h | 26 ---- src/grid/GridExperiment.cpp | 8 +- src/grid/GridExperiment.h | 19 +-- src/grid/GridFunctions.cpp | 240 ------------------------------------ src/grid/GridSearch.cpp | 4 +- src/grid/GridSearch.h | 12 +- 8 files changed, 30 insertions(+), 293 deletions(-) delete mode 100644 src/grid/GridFunctions.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f0f9835..62a7d9a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,7 +29,7 @@ add_executable( target_link_libraries(b_best Boost::boost "${PyClassifiers}" "${BayesNet}" fimdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy "${XLSXWRITER_LIB}") # b_grid -set(grid_sources GridSearch.cpp GridData.cpp GridExperiment.cpp GridFunctions.cpp) +set(grid_sources GridSearch.cpp GridData.cpp GridExperiment.cpp) list(TRANSFORM grid_sources PREPEND grid/) add_executable(b_grid commands/b_grid.cpp ${grid_sources} common/Datasets.cpp common/Dataset.cpp common/Discretization.cpp diff --git a/src/grid/GridBase.h b/src/grid/GridBase.h index 0f4484d..66343a0 100644 --- a/src/grid/GridBase.h +++ b/src/grid/GridBase.h @@ -32,12 +32,22 @@ namespace platform { }; ~GridBase() = default; virtual void go(struct ConfigMPI& config_mpi) = 0; - virtual json build_tasks_mpi() = 0; protected: + virtual json build_tasks() = 0; struct ConfigGrid config; Timer timer; // used to measure the time of the whole process const std::string separator = "|"; bayesnet::Smoothing_t smooth_type{ bayesnet::Smoothing_t::NONE }; }; + class MPI_Base { + public: + static std::string get_color_rank(int rank) + { + auto colors = { Colors::WHITE(), Colors::RED(), Colors::GREEN(), Colors::BLUE(), Colors::MAGENTA(), Colors::CYAN(), Colors::YELLOW(), Colors::BLACK() }; + std::string id = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + auto idx = rank % id.size(); + return *(colors.begin() + rank % colors.size()) + id[idx]; + } + }; } /* namespace platform */ #endif \ No newline at end of file diff --git a/src/grid/GridConfig.h b/src/grid/GridConfig.h index b077678..ee9065e 100644 --- a/src/grid/GridConfig.h +++ b/src/grid/GridConfig.h @@ -45,31 +45,5 @@ namespace platform { const int TAG_RESULT = 2; const int TAG_TASK = 3; const int TAG_END = 4; - /* ************************************************************************************************************* - // - // MPI Common Functions - // - ************************************************************************************************************* */ - std::string get_color_rank(int rank); - /* ************************************************************************************************************* - // - // MPI Experiment Functions - // - ************************************************************************************************************* */ - json mpi_experiment_producer(std::vector& names, json& tasks, struct ConfigMPI& config_mpi, MPI_Datatype& MPI_Result); - void mpi_experiment_consumer(Datasets& datasets, json& tasks, struct ConfigGrid& config, struct ConfigMPI& config_mpi, MPI_Datatype& MPI_Result); - void join_results_folds(json& results, json& all_results, std::string& model); - json store_experiment_result(std::vector& names, Task_Result& result, json& results); - void mpi_experiment_consumer_go(struct ConfigGrid& config, struct ConfigMPI& config_mpi, json& tass, int n_task, Datasets& datasets, Task_Result* result); - /* ************************************************************************************************************* - // - // MPI Search Functions - // - ************************************************************************************************************* */ - json mpi_search_producer(std::vector& names, json& tasks, struct ConfigMPI& config_mpi, MPI_Datatype& MPI_Result); - void mpi_search_consumer(Datasets& datasets, json& tasks, struct ConfigGrid& config, struct ConfigMPI& config_mpi, MPI_Datatype& MPI_Result); - void select_best_results_folds(json& results, json& all_results, std::string& model); - json store_search_result(std::vector& names, Task_Result& result, json& results); - void mpi_search_consumer_go(struct ConfigGrid& config, struct ConfigMPI& config_mpi, json& tasks, int n_task, Datasets& datasets, Task_Result* result); } /* namespace platform */ #endif \ No newline at end of file diff --git a/src/grid/GridExperiment.cpp b/src/grid/GridExperiment.cpp index b9d290f..8baec34 100644 --- a/src/grid/GridExperiment.cpp +++ b/src/grid/GridExperiment.cpp @@ -21,7 +21,7 @@ namespace platform { } return json(); } - json GridExperiment::build_tasks_mpi() + json GridExperiment::build_tasks() { auto tasks = json::array(); auto grid = GridData(Paths::grid_input(config.model)); @@ -113,7 +113,7 @@ namespace platform { json tasks; if (config_mpi.rank == config_mpi.manager) { timer.start(); - tasks = build_tasks_mpi(); + tasks = build_tasks(); auto tasks_str = tasks.dump(); tasks_size = tasks_str.size(); msg = new char[tasks_size + 1]; @@ -137,7 +137,7 @@ namespace platform { // 2a. Producer delivers the tasks to the consumers // auto datasets_names = std::vector(); - json all_results = mpi_experiment_producer(datasets_names, tasks, config_mpi, MPI_Result); + json all_results = MPI_EXPERIMENT::producer(datasets_names, tasks, config_mpi, MPI_Result); std::cout << separator << std::endl; // // 3. Manager select the bests sccores for each dataset @@ -152,7 +152,7 @@ namespace platform { // // 2b. Consumers prostore_search_resultcess the tasks and send the results to the producer // - mpi_experiment_consumer(datasets, tasks, config, config_mpi, MPI_Result); + MPI_EXPERIMENT::consumer(datasets, tasks, config, config_mpi, MPI_Result); } } json GridExperiment::initializeResults() diff --git a/src/grid/GridExperiment.h b/src/grid/GridExperiment.h index 13b5ef3..6a3b7bb 100644 --- a/src/grid/GridExperiment.h +++ b/src/grid/GridExperiment.h @@ -23,22 +23,15 @@ namespace platform { private: void save(json& results); json initializeResults(); - json build_tasks_mpi(); + json build_tasks(); }; /* ************************************************************************************************************* // // MPI Search Functions // ************************************************************************************************************* */ - class MPI_EXPERIMENT { + class MPI_EXPERIMENT :public MPI_Base { public: - static std::string get_color_rank(int rank) - { - auto colors = { Colors::WHITE(), Colors::RED(), Colors::GREEN(), Colors::BLUE(), Colors::MAGENTA(), Colors::CYAN(), Colors::YELLOW(), Colors::BLACK() }; - std::string id = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - auto idx = rank % id.size(); - return *(colors.begin() + rank % colors.size()) + id[idx]; - } static json producer(std::vector& names, json& tasks, struct ConfigMPI& config_mpi, MPI_Datatype& MPI_Result) { Task_Result result; @@ -53,7 +46,7 @@ namespace platform { MPI_Recv(&result, 1, MPI_Result, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); if (status.MPI_TAG == TAG_RESULT) { //Store result - store_search_result(names, result, results); + store_result(names, result, results); } MPI_Send(&i, 1, MPI_INT, status.MPI_SOURCE, TAG_TASK, MPI_COMM_WORLD); } @@ -65,7 +58,7 @@ namespace platform { MPI_Recv(&result, 1, MPI_Result, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); if (status.MPI_TAG == TAG_RESULT) { //Store result - store_search_result(names, result, results); + store_result(names, result, results); } MPI_Send(&i, 1, MPI_INT, status.MPI_SOURCE, TAG_END, MPI_COMM_WORLD); } @@ -88,7 +81,7 @@ namespace platform { if (status.MPI_TAG == TAG_END) { break; } - mpi_experiment_consumer_go(config, config_mpi, tasks, task, datasets, &result); + consumer_go(config, config_mpi, tasks, task, datasets, &result); // // 2b.3 Consumers send the result to the producer // @@ -125,7 +118,7 @@ namespace platform { results[dataset] = json_best; } } - static json store_search_result(std::vector& names, Task_Result& result, json& results) + static json store_result(std::vector& names, Task_Result& result, json& results) { json json_result = { { "score", result.score }, diff --git a/src/grid/GridFunctions.cpp b/src/grid/GridFunctions.cpp deleted file mode 100644 index 9b81247..0000000 --- a/src/grid/GridFunctions.cpp +++ /dev/null @@ -1,240 +0,0 @@ - -#include -#include -#include -#include "main/Models.h" -#include "common/Paths.h" -#include "common/Colors.h" -#include "common/Utils.h" -#include "GridConfig.h" -namespace platform { - using json = nlohmann::ordered_json; - std::string get_color_rank(int rank) - { - auto colors = { Colors::WHITE(), Colors::RED(), Colors::GREEN(), Colors::BLUE(), Colors::MAGENTA(), Colors::CYAN(), Colors::YELLOW(), Colors::BLACK() }; - std::string id = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - auto idx = rank % id.size(); - return *(colors.begin() + rank % colors.size()) + id[idx]; - } - /* ************************************************************************************************************* - // - // MPI Experiment Functions - // - ************************************************************************************************************* */ - json mpi_experiment_producer(std::vector& names, json& tasks, struct ConfigMPI& config_mpi, MPI_Datatype& MPI_Result) - { - Task_Result result; - json results; - int num_tasks = tasks.size(); - - // - // 2a.1 Producer will loop to send all the tasks to the consumers and receive the results - // - for (int i = 0; i < num_tasks; ++i) { - MPI_Status status; - MPI_Recv(&result, 1, MPI_Result, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); - if (status.MPI_TAG == TAG_RESULT) { - //Store result - store_experiment_result(names, result, results); - } - MPI_Send(&i, 1, MPI_INT, status.MPI_SOURCE, TAG_TASK, MPI_COMM_WORLD); - } - // - // 2a.2 Producer will send the end message to all the consumers - // - for (int i = 0; i < config_mpi.n_procs - 1; ++i) { - MPI_Status status; - MPI_Recv(&result, 1, MPI_Result, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); - if (status.MPI_TAG == TAG_RESULT) { - //Store result - store_experiment_result(names, result, results); - } - MPI_Send(&i, 1, MPI_INT, status.MPI_SOURCE, TAG_END, MPI_COMM_WORLD); - } - return results; - } - void mpi_experiment_consumer(Datasets& datasets, json& tasks, struct ConfigGrid& config, struct ConfigMPI& config_mpi, MPI_Datatype& MPI_Result) - { - Task_Result result; - // - // 2b.1 Consumers announce to the producer that they are ready to receive a task - // - MPI_Send(&result, 1, MPI_Result, config_mpi.manager, TAG_QUERY, MPI_COMM_WORLD); - int task; - while (true) { - MPI_Status status; - // - // 2b.2 Consumers receive the task from the producer and process it - // - MPI_Recv(&task, 1, MPI_INT, config_mpi.manager, MPI_ANY_TAG, MPI_COMM_WORLD, &status); - if (status.MPI_TAG == TAG_END) { - break; - } - mpi_experiment_consumer_go(config, config_mpi, tasks, task, datasets, &result); - // - // 2b.3 Consumers send the result to the producer - // - MPI_Send(&result, 1, MPI_Result, config_mpi.manager, TAG_RESULT, MPI_COMM_WORLD); - } - } - void join_results_folds(json& results, json& all_results, std::string& model) - { - Timer timer; - auto grid = GridData(Paths::grid_input(model)); - // - // Select the best result of the computed outer folds - // - for (const auto& result : all_results.items()) { - // each result has the results of all the outer folds as each one were a different task - double best_score = 0.0; - json best; - for (const auto& result_fold : result.value()) { - double score = result_fold["score"].get(); - if (score > best_score) { - best_score = score; - best = result_fold; - } - } - auto dataset = result.key(); - auto combinations = grid.getGrid(dataset); - json json_best = { - { "score", best_score }, - { "hyperparameters", combinations[best["combination"].get()] }, - { "date", get_date() + " " + get_time() }, - { "grid", grid.getInputGrid(dataset) }, - { "duration", timer.translate2String(best["time"].get()) } - }; - results[dataset] = json_best; - } - } - json store_experiment_result(std::vector& names, Task_Result& result, json& results) - { - json json_result = { - { "score", result.score }, - { "combination", result.idx_combination }, - { "fold", result.n_fold }, - { "time", result.time }, - { "dataset", result.idx_dataset } - }; - auto name = names[result.idx_dataset]; - if (!results.contains(name)) { - results[name] = json::array(); - } - results[name].push_back(json_result); - return results; - } - void mpi_experiment_consumer_go(struct ConfigGrid& config, struct ConfigMPI& config_mpi, json& tasks, int n_task, Datasets& datasets, Task_Result* result) - { - // - // initialize - // - Timer timer; - timer.start(); - json task = tasks[n_task]; - auto model = config.model; - auto grid = GridData(Paths::grid_input(model)); - auto dataset_name = task["dataset"].get(); - auto idx_dataset = task["idx_dataset"].get(); - auto seed = task["seed"].get(); - auto n_fold = task["fold"].get(); - bool stratified = config.stratified; - bayesnet::Smoothing_t smooth; - if (config.smooth_strategy == "ORIGINAL") - smooth = bayesnet::Smoothing_t::ORIGINAL; - else if (config.smooth_strategy == "LAPLACE") - smooth = bayesnet::Smoothing_t::LAPLACE; - else if (config.smooth_strategy == "CESTNIK") - smooth = bayesnet::Smoothing_t::CESTNIK; - // - // Generate the hyperparameters combinations - // - auto& dataset = datasets.getDataset(dataset_name); - auto combinations = grid.getGrid(dataset_name); - dataset.load(); - auto [X, y] = dataset.getTensors(); - auto features = dataset.getFeatures(); - auto className = dataset.getClassName(); - // - // Start working on task - // - folding::Fold* fold; - if (stratified) - fold = new folding::StratifiedKFold(config.n_folds, y, seed); - else - fold = new folding::KFold(config.n_folds, y.size(0), seed); - auto [train, test] = fold->getFold(n_fold); - auto [X_train, X_test, y_train, y_test] = dataset.getTrainTestTensors(train, test); - auto states = dataset.getStates(); // Get the states of the features Once they are discretized - float best_fold_score = 0.0; - int best_idx_combination = -1; - json best_fold_hyper; - for (int idx_combination = 0; idx_combination < combinations.size(); ++idx_combination) { - auto hyperparam_line = combinations[idx_combination]; - auto hyperparameters = platform::HyperParameters(datasets.getNames(), hyperparam_line); - folding::Fold* nested_fold; - if (config.stratified) - nested_fold = new folding::StratifiedKFold(config.nested, y_train, seed); - else - nested_fold = new folding::KFold(config.nested, y_train.size(0), seed); - double score = 0.0; - for (int n_nested_fold = 0; n_nested_fold < config.nested; n_nested_fold++) { - // - // Nested level fold - // - auto [train_nested, test_nested] = nested_fold->getFold(n_nested_fold); - auto train_nested_t = torch::tensor(train_nested); - auto test_nested_t = torch::tensor(test_nested); - auto X_nested_train = X_train.index({ "...", train_nested_t }); - auto y_nested_train = y_train.index({ train_nested_t }); - auto X_nested_test = X_train.index({ "...", test_nested_t }); - auto y_nested_test = y_train.index({ test_nested_t }); - // - // Build Classifier with selected hyperparameters - // - auto clf = Models::instance()->create(config.model); - auto valid = clf->getValidHyperparameters(); - hyperparameters.check(valid, dataset_name); - clf->setHyperparameters(hyperparameters.get(dataset_name)); - // - // Train model - // - clf->fit(X_nested_train, y_nested_train, features, className, states, smooth); - // - // Test model - // - score += clf->score(X_nested_test, y_nested_test); - } - delete nested_fold; - score /= config.nested; - if (score > best_fold_score) { - best_fold_score = score; - best_idx_combination = idx_combination; - best_fold_hyper = hyperparam_line; - } - } - delete fold; - // - // Build Classifier with the best hyperparameters to obtain the best score - // - auto hyperparameters = platform::HyperParameters(datasets.getNames(), best_fold_hyper); - auto clf = Models::instance()->create(config.model); - auto valid = clf->getValidHyperparameters(); - hyperparameters.check(valid, dataset_name); - clf->setHyperparameters(best_fold_hyper); - clf->fit(X_train, y_train, features, className, states, smooth); - best_fold_score = clf->score(X_test, y_test); - // - // Return the result - // - result->idx_dataset = task["idx_dataset"].get(); - result->idx_combination = best_idx_combination; - result->score = best_fold_score; - result->n_fold = n_fold; - result->time = timer.getDuration(); - // - // Update progress bar - // - std::cout << get_color_rank(config_mpi.rank) << std::flush; - } - -} \ No newline at end of file diff --git a/src/grid/GridSearch.cpp b/src/grid/GridSearch.cpp index 51ab895..9bb6574 100644 --- a/src/grid/GridSearch.cpp +++ b/src/grid/GridSearch.cpp @@ -53,7 +53,7 @@ namespace platform { } return datasets_names; } - json GridSearch::build_tasks_mpi() + json GridSearch::build_tasks() { auto tasks = json::array(); auto grid = GridData(Paths::grid_input(config.model)); @@ -145,7 +145,7 @@ namespace platform { json tasks; if (config_mpi.rank == config_mpi.manager) { timer.start(); - tasks = build_tasks_mpi(); + tasks = build_tasks(); auto tasks_str = tasks.dump(); tasks_size = tasks_str.size(); msg = new char[tasks_size + 1]; diff --git a/src/grid/GridSearch.h b/src/grid/GridSearch.h index 3735a5f..a30d148 100644 --- a/src/grid/GridSearch.h +++ b/src/grid/GridSearch.h @@ -26,14 +26,14 @@ namespace platform { void save(json& results); json initializeResults(); std::vector filterDatasets(Datasets& datasets) const; - json build_tasks_mpi(); + json build_tasks(); }; /* ************************************************************************************************************* // // MPI Search Functions // ************************************************************************************************************* */ - class MPI_SEARCH { + class MPI_SEARCH :public MPI_Base { public: static json producer(std::vector& names, json& tasks, struct ConfigMPI& config_mpi, MPI_Datatype& MPI_Result) { @@ -49,7 +49,7 @@ namespace platform { MPI_Recv(&result, 1, MPI_Result, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); if (status.MPI_TAG == TAG_RESULT) { //Store result - store_search_result(names, result, results); + store_result(names, result, results); } MPI_Send(&i, 1, MPI_INT, status.MPI_SOURCE, TAG_TASK, MPI_COMM_WORLD); } @@ -61,7 +61,7 @@ namespace platform { MPI_Recv(&result, 1, MPI_Result, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); if (status.MPI_TAG == TAG_RESULT) { //Store result - store_search_result(names, result, results); + store_result(names, result, results); } MPI_Send(&i, 1, MPI_INT, status.MPI_SOURCE, TAG_END, MPI_COMM_WORLD); } @@ -84,7 +84,7 @@ namespace platform { if (status.MPI_TAG == TAG_END) { break; } - mpi_experiment_consumer_go(config, config_mpi, tasks, task, datasets, &result); + consumer_go(config, config_mpi, tasks, task, datasets, &result); // // 2b.3 Consumers send the result to the producer // @@ -121,7 +121,7 @@ namespace platform { results[dataset] = json_best; } } - static json store_search_result(std::vector& names, Task_Result& result, json& results) + static json store_result(std::vector& names, Task_Result& result, json& results) { json json_result = { { "score", result.score },