From 2715a1f65ee537c308f9336225b3e832a045617d Mon Sep 17 00:00:00 2001 From: Abdoulbari Zaher <32519851+a-zakir@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:32:02 +0100 Subject: [PATCH] [Ant-2380] improving maintainability (#965) This pull request includes several changes to the `BendersBase` class and related components to improve the handling of criterion computations and refactor the codebase. The most important changes include the renaming of `OuterLoopInputData` to `CriterionInputData`, the addition of the `CouplingMapGenerator` class, and the refactoring of methods related to criterion computations. ### Refactoring and Renaming: * Renamed `OuterLoopInputData` to `CriterionInputData` and updated all related references in `CriterionComputation.cpp` and `CriterionInputDataReader.cpp` [[1]](diffhunk://#diff-d0e3218aceff8618ccc9bf1b8ee0ed104175f6db50b089e0e2fd46c4372daf88L32-R44) [[2]](diffhunk://#diff-68cda14d5a160cbb0d388b11de54f391a6bf6aa2a2b46ee4f97dba03c963fedcL1-R1) [[3]](diffhunk://#diff-68cda14d5a160cbb0d388b11de54f391a6bf6aa2a2b46ee4f97dba03c963fedcL13-R83). * Removed the `OuterloopOptionsFile` method from `BendersBase` and updated the `setCriterionsComputation` method to `setCriterionComputationInputs` [[1]](diffhunk://#diff-debcbcbc301c4e3ac700146a75db0a05d686e1d30f20ce42019e1e77acf903a5L25-L29) [[2]](diffhunk://#diff-36a63705b7409199b9d45a79b1f80f656b02e9f5a4d1a2c41703742ad41a76c2L98-R103) [[3]](diffhunk://#diff-36a63705b7409199b9d45a79b1f80f656b02e9f5a4d1a2c41703742ad41a76c2L226-R225). ### Addition of CouplingMapGenerator: * Added a new `CouplingMapGenerator` class to handle the building of input maps from structure files, encapsulating this functionality [[1]](diffhunk://#diff-e5c5805ed18be849a2bcfcaf5f3a459394022162fe8c0b168ad0164688aac93eR1-R52) [[2]](diffhunk://#diff-2a07bb398e3d2d00e5899c2b0c02ae4da892bdcf20158651d1a4ce4c1a7b49c3R1-R9). ### Updates to Criterion Computation: * Refactored `CriterionComputation` to use `CriterionInputData` instead of `OuterLoopInputData`, updating methods and member variables accordingly [[1]](diffhunk://#diff-d0e3218aceff8618ccc9bf1b8ee0ed104175f6db50b089e0e2fd46c4372daf88L32-R44) [[2]](diffhunk://#diff-debcbcbc301c4e3ac700146a75db0a05d686e1d30f20ce42019e1e77acf903a5L420-R415) [[3]](diffhunk://#diff-debcbcbc301c4e3ac700146a75db0a05d686e1d30f20ce42019e1e77acf903a5R1045-R1054). ### CMakeLists and File Updates: * Updated `CMakeLists.txt` to reflect the renaming of files and the addition of new source files (`CouplingMapGenerator.cpp`) [[1]](diffhunk://#diff-89a10ccfdde5ce80ade12658a06704594fe58b1f86f162a55bc68252f382ff09L26-R28) [[2]](diffhunk://#diff-89a10ccfdde5ce80ade12658a06704594fe58b1f86f162a55bc68252f382ff09L44-L50). ### Removal of Unused Methods: * Removed the `build_input` method from `common.cpp` as its functionality is now encapsulated in `CouplingMapGenerator`. --- .../get-started/adequacy-criterion.md | 2 +- .../benders_by_batch/BendersByBatch.cpp | 2 +- src/cpp/benders/benders_core/BendersBase.cpp | 58 +++--- .../benders_core/BendersMathLogger.cpp | 14 +- src/cpp/benders/benders_core/CMakeLists.txt | 7 +- .../benders_core/CouplingMapGenerator.cpp | 52 +++++ .../benders_core/CriterionComputation.cpp | 52 ++--- ...eader.cpp => CriterionInputDataReader.cpp} | 122 ++++++------ .../benders/benders_core/VariablesGroup.cpp | 8 +- src/cpp/benders/benders_core/common.cpp | 36 ---- .../benders/benders_core/BendersBase.h | 21 +- .../benders/benders_core/BendersMathLogger.h | 12 +- .../benders_core/BendersStructsDatas.h | 14 +- .../benders_core/CouplingMapGenerator.h | 9 + .../benders_core/CriterionComputation.h | 43 ++-- ...ataReader.h => CriterionInputDataReader.h} | 87 ++++---- .../benders/benders_core/SubproblemCut.h | 6 +- .../benders/benders_core/VariablesGroup.h | 8 +- .../benders/benders_core/common.h | 1 - src/cpp/benders/benders_mpi/BendersMPI.cpp | 80 ++++---- .../benders_mpi/BendersMpiOuterLoop.cpp | 2 +- .../benders/benders_mpi/OuterLoopBenders.cpp | 4 +- .../benders/benders_mpi/BendersMPI.h | 2 +- .../benders/benders_mpi/OuterLoopBenders.h | 2 +- src/cpp/benders/factories/BendersFactory.cpp | 188 ++++++++++-------- src/cpp/benders/factories/LoggerFactories.cpp | 8 - .../benders/factories/BendersFactory.h | 30 ++- .../benders/factories/LoggerFactories.h | 3 - src/cpp/benders/merge_mps/MergeMPS.cpp | 6 +- .../benders/outer_loop/OuterLoopBiLevel.cpp | 2 +- .../benders/outer_loop/OuterLoopBiLevel.h | 6 +- .../xpansion_interfaces/LogUtils.h | 4 +- tests/cpp/outer_loop/outer_loop_test.cpp | 97 ++++----- 33 files changed, 530 insertions(+), 458 deletions(-) create mode 100644 src/cpp/benders/benders_core/CouplingMapGenerator.cpp rename src/cpp/benders/benders_core/{OuterLoopInputDataReader.cpp => CriterionInputDataReader.cpp} (51%) create mode 100644 src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CouplingMapGenerator.h rename src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/{OuterLoopInputDataReader.h => CriterionInputDataReader.h} (51%) diff --git a/docs/user-guide/get-started/adequacy-criterion.md b/docs/user-guide/get-started/adequacy-criterion.md index 9f92c2520..6f0902447 100644 --- a/docs/user-guide/get-started/adequacy-criterion.md +++ b/docs/user-guide/get-started/adequacy-criterion.md @@ -47,5 +47,5 @@ Several log files are written: - `reportbenders.txt` gives information on the progress of the algorithm with an operational perspective, - `benders_solver.log` contains more detailed information on all data of interest to follow the progress of the algorithm (`lambda_min`, `lambda_max`, detailed solving times, ...). -- The file `criterions.txt` under `lp/` dir stores adequacy criterions for all valid patterns (area+criterion) +- The file `LOLD.txt` under `lp/` dir stores adequacy criteria for all valid patterns (area+criterion) - The file `PositiveUnsuppliedEnergy.txt` under `lp/` dir stores the amount of unsupplied energy for all valid patterns (area+criterion) diff --git a/src/cpp/benders/benders_by_batch/BendersByBatch.cpp b/src/cpp/benders/benders_by_batch/BendersByBatch.cpp index 0680aeb06..574f2cb51 100644 --- a/src/cpp/benders/benders_by_batch/BendersByBatch.cpp +++ b/src/cpp/benders/benders_by_batch/BendersByBatch.cpp @@ -153,7 +153,7 @@ void BendersByBatch::SeparationLoop() { SolveBatches(); if (Rank() == rank_0) { - outer_loop_criterion_.push_back(_data.outer_loop_current_iteration_data.outer_loop_criterion); + criteria_vector_for_each_iteration_.push_back(_data.criteria_current_iteration_data.criteria); // TODO // UpdateOuterLoopMaxCriterionArea(); UpdateTrace(); diff --git a/src/cpp/benders/benders_core/BendersBase.cpp b/src/cpp/benders/benders_core/BendersBase.cpp index 04e1a806f..5df28bfb1 100644 --- a/src/cpp/benders/benders_core/BendersBase.cpp +++ b/src/cpp/benders/benders_core/BendersBase.cpp @@ -22,11 +22,6 @@ BendersBase::BendersBase(const BendersBaseOptions &options, Logger logger, mathLoggerDriver_(std::move(mathLoggerDriver)) { } -std::filesystem::path BendersBase::OuterloopOptionsFile() const { - return std::filesystem::path(_options.INPUTROOT) / - _options.EXTERNAL_LOOP_OPTIONS.OUTER_LOOP_OPTION_FILE; -} - /*! * \brief Initialize set of data used in the loop */ @@ -47,7 +42,7 @@ void BendersBase::init_data() { _data.iteration_time = 0; _data.timer_master = 0; _data.subproblems_walltime = 0; - outer_loop_criterion_.clear(); + criteria_vector_for_each_iteration_.clear(); } void BendersBase::OpenCsvFile() { @@ -173,10 +168,10 @@ void BendersBase::update_best_ub() { _data.best_ub = _data.ub; _data.best_it = _data.it; FillWorkerMasterData(relevantIterationData_.best); - _data.outer_loop_current_iteration_data.max_criterion_best_it = - _data.outer_loop_current_iteration_data.max_criterion; - _data.outer_loop_current_iteration_data.max_criterion_area_best_it = - _data.outer_loop_current_iteration_data.max_criterion_area; + _data.criteria_current_iteration_data.max_criterion_best_it = + _data.criteria_current_iteration_data.max_criterion; + _data.criteria_current_iteration_data.max_criterion_area_best_it = + _data.criteria_current_iteration_data.max_criterion_area; relevantIterationData_.best._cut_trace = relevantIterationData_.last._cut_trace; best_iteration_data = bendersDataToLogData(_data); } @@ -417,7 +412,7 @@ void BendersBase::SetSubproblemsVariablesIndices() { if (!subproblem_map.empty()) { auto subproblem = subproblem_map.begin(); - criterions_computation_->SearchVariables( + criterion_computation_.SearchVariables( subproblem->second->_solver->get_col_names()); } } @@ -539,7 +534,7 @@ void BendersBase::SaveCurrentOuterLoopIterationInOutputFile() const { if (LastWorkerMasterData._valid) { _writer->write_iteration( iteration(LastWorkerMasterData), - _data.outer_loop_current_iteration_data.benders_num_run); + _data.criteria_current_iteration_data.benders_num_run); _writer->dump(); } } @@ -599,7 +594,7 @@ Output::SolutionData BendersBase::solution() const { void BendersBase::UpdateOuterLoopSolution() { outer_loop_solution_data_ = BendersSolution(); outer_loop_solution_data_.best_it = - _data.outer_loop_current_iteration_data.benders_num_run; + _data.criteria_current_iteration_data.benders_num_run; } Output::SolutionData BendersBase::GetOuterLoopSolution() const { @@ -1000,13 +995,13 @@ WorkerMasterData BendersBase::BestIterationWorkerMaster() const { CurrentIterationData BendersBase::GetCurrentIterationData() const { return _data; } -OuterLoopCurrentIterationData BendersBase::GetOuterLoopData() const { - return _data.outer_loop_current_iteration_data; +CriteriaCurrentIterationData BendersBase::GetOuterLoopData() const { + return _data.criteria_current_iteration_data; } std::vector BendersBase::GetOuterLoopCriterionAtBestBenders() const { - return ((outer_loop_criterion_.empty()) + return ((criteria_vector_for_each_iteration_.empty()) ? std::vector() - : outer_loop_criterion_[_data.best_it - 1]); + : criteria_vector_for_each_iteration_[_data.best_it - 1]); } void BendersBase::init_data(double external_loop_lambda, @@ -1014,25 +1009,26 @@ void BendersBase::init_data(double external_loop_lambda, double external_loop_lambda_max) { benders_timer.restart(); auto benders_num_run = - _data.outer_loop_current_iteration_data.benders_num_run; + _data.criteria_current_iteration_data.benders_num_run; auto outer_loop_bilevel_best_ub = - _data.outer_loop_current_iteration_data.outer_loop_bilevel_best_ub; + _data.criteria_current_iteration_data.outer_loop_bilevel_best_ub; init_data(); - _data.outer_loop_current_iteration_data.outer_loop_criterion.clear(); - _data.outer_loop_current_iteration_data.benders_num_run = benders_num_run; - _data.outer_loop_current_iteration_data.outer_loop_bilevel_best_ub = + _data.criteria_current_iteration_data.criteria.clear(); + _data.criteria_current_iteration_data.benders_num_run = benders_num_run; + _data.criteria_current_iteration_data.outer_loop_bilevel_best_ub = outer_loop_bilevel_best_ub; - _data.outer_loop_current_iteration_data.external_loop_lambda = + _data.criteria_current_iteration_data.lambda = external_loop_lambda; - _data.outer_loop_current_iteration_data.external_loop_lambda_min = + _data.criteria_current_iteration_data.lambda_min = external_loop_lambda_min; - _data.outer_loop_current_iteration_data.external_loop_lambda_max = + _data.criteria_current_iteration_data.lambda_max = external_loop_lambda_max; } bool BendersBase::isExceptionRaised() const { return exception_raised_; } + /* * after the 1st loop of the outer loop, we must re-build the objective * function and costs @@ -1046,12 +1042,14 @@ void BendersBase::UpdateOverallCosts() { relevantIterationData_.best._invest_cost = _data.invest_cost; } + void BendersBase::SetBilevelBestub(double bilevel_best_ub) { - _data.outer_loop_current_iteration_data.outer_loop_bilevel_best_ub = + _data.criteria_current_iteration_data.outer_loop_bilevel_best_ub = bilevel_best_ub; } -void BendersBase::setCriterionsComputation( - std::shared_ptr - criterionsComputation) { - criterions_computation_ = criterionsComputation; + +void BendersBase::setCriterionComputationInputs( + const Benders::Criterion::CriterionInputData &criterion_input_data) { + criterion_computation_ = + Benders::Criterion::CriterionComputation(criterion_input_data); } diff --git a/src/cpp/benders/benders_core/BendersMathLogger.cpp b/src/cpp/benders/benders_core/BendersMathLogger.cpp index 143c9885f..b60355cc8 100644 --- a/src/cpp/benders/benders_core/BendersMathLogger.cpp +++ b/src/cpp/benders/benders_core/BendersMathLogger.cpp @@ -216,29 +216,29 @@ void PrintExternalLoopData(LogDestination& log_destination, const HEADERSTYPE& type, const BENDERSMETHOD& method) { log_destination.InsertDelimiter(); - log_destination << data.outer_loop_current_iteration_data.benders_num_run; + log_destination << data.criteria_current_iteration_data.benders_num_run; log_destination.InsertDelimiter(); log_destination << std::scientific << std::setprecision(10) - << data.outer_loop_current_iteration_data.max_criterion; + << data.criteria_current_iteration_data.max_criterion; log_destination.InsertDelimiter(); - log_destination << data.outer_loop_current_iteration_data.max_criterion_area; + log_destination << data.criteria_current_iteration_data.max_criterion_area; log_destination.InsertDelimiter(); log_destination << std::scientific << std::setprecision(10) - << data.outer_loop_current_iteration_data.outer_loop_bilevel_best_ub; + << data.criteria_current_iteration_data.outer_loop_bilevel_best_ub; log_destination.InsertDelimiter(); log_destination << std::scientific << std::setprecision(10) - << data.outer_loop_current_iteration_data.external_loop_lambda; + << data.criteria_current_iteration_data.lambda; log_destination.InsertDelimiter(); log_destination << std::scientific << std::setprecision(10) - << data.outer_loop_current_iteration_data.external_loop_lambda_min; + << data.criteria_current_iteration_data.lambda_min; log_destination.InsertDelimiter(); log_destination << std::scientific << std::setprecision(10) - << data.outer_loop_current_iteration_data.external_loop_lambda_max; + << data.criteria_current_iteration_data.lambda_max; PrintBendersData(log_destination, data, type, method); } void MathLoggerBaseExternalLoop::Print(const CurrentIterationData& data) { diff --git a/src/cpp/benders/benders_core/CMakeLists.txt b/src/cpp/benders/benders_core/CMakeLists.txt index fd236852e..966393309 100644 --- a/src/cpp/benders/benders_core/CMakeLists.txt +++ b/src/cpp/benders/benders_core/CMakeLists.txt @@ -23,8 +23,9 @@ target_sources(benders_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/WorkerMaster.cpp ${CMAKE_CURRENT_SOURCE_DIR}/common.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CriterionComputation.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/OuterLoopInputDataReader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/CriterionInputDataReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/VariablesGroup.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/CouplingMapGenerator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/include/antares-xpansion/benders/benders_core/BendersBase.h ${CMAKE_CURRENT_SOURCE_DIR}/include/antares-xpansion/benders/benders_core/BendersMathLogger.h ${CMAKE_CURRENT_SOURCE_DIR}/include/antares-xpansion/benders/benders_core/BendersStructsDatas.h @@ -41,13 +42,13 @@ target_sources(benders_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/antares-xpansion/benders/benders_core/WorkerMaster.h ${CMAKE_CURRENT_SOURCE_DIR}/include/antares-xpansion/benders/benders_core/common.h ${CMAKE_CURRENT_SOURCE_DIR}/include/antares-xpansion/benders/benders_core/CriterionComputation.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/antares-xpansion/benders/benders_core/OuterLoopInputDataReader.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/antares-xpansion/benders/benders_core/CriterionInputDataReader.h ${CMAKE_CURRENT_SOURCE_DIR}/include/antares-xpansion/benders/benders_core/VariablesGroup.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/antares-xpansion/benders/benders_core/CouplingMapGenerator.h ) add_library(antaresXpansion::benders_core ALIAS benders_core) - target_include_directories(benders_core PUBLIC $ diff --git a/src/cpp/benders/benders_core/CouplingMapGenerator.cpp b/src/cpp/benders/benders_core/CouplingMapGenerator.cpp new file mode 100644 index 000000000..9780b4664 --- /dev/null +++ b/src/cpp/benders/benders_core/CouplingMapGenerator.cpp @@ -0,0 +1,52 @@ +#include +#include + +struct InvalidStructureFile : LogUtils::XpansionError { + using LogUtils::XpansionError::XpansionError; +}; + +/*! + * \brief Build the input from the structure file + * + * Function to build the map linking each problem name to its variables and + *their id + * + * \param root : root of the structure file + * + * \param summary_name : name of the structure file + * + * \param coupling_map : empty map to increment + * + * \note The id in the coupling_map is that of the variable in the solver + *responsible for the creation of the structure file. + */ +CouplingMap CouplingMapGenerator::BuildInput( + const std::filesystem::path& structure_path, + std::shared_ptr logger, const std::string& context) { + CouplingMap coupling_map; + std::ifstream summary(structure_path, std::ios::in); + if (!summary) { + auto log_location = LOGLOCATION; + std::ostringstream msg; + msg << "Cannot open structure file " << structure_path << std::endl; + logger->display_message(msg.str(), LogUtils::LOGLEVEL::FATAL, log_location); + throw InvalidStructureFile( + PrefixMessage(LogUtils::LOGLEVEL::FATAL, context), msg.str(), + log_location); + } + std::string line; + + while (std::getline(summary, line)) { + std::stringstream buffer(line); + std::string problem_name; + std::string variable_name; + int variable_id; + buffer >> problem_name; + buffer >> variable_name; + buffer >> variable_id; + coupling_map[problem_name][variable_name] = variable_id; + } + + summary.close(); + return coupling_map; +} \ No newline at end of file diff --git a/src/cpp/benders/benders_core/CriterionComputation.cpp b/src/cpp/benders/benders_core/CriterionComputation.cpp index 72d65fc97..0033da69e 100644 --- a/src/cpp/benders/benders_core/CriterionComputation.cpp +++ b/src/cpp/benders/benders_core/CriterionComputation.cpp @@ -2,46 +2,46 @@ namespace Benders::Criterion { -void CriterionComputation::ComputeOuterLoopCriterion( +void CriterionComputation::ComputeCriterion( double subproblem_weight, const std::vector &sub_problem_solution, - std::vector &outerLoopCriterions, - std::vector &outerLoopPatternsValues) { - auto outer_loop_input_size = var_indices_.size(); // num of patterns - outerLoopCriterions.resize(outer_loop_input_size, 0.); - outerLoopPatternsValues.resize(outer_loop_input_size, 0.); - - double criterion_count_threshold = - outer_loop_input_data_.CriterionCountThreshold(); - - for (int pattern_index(0); pattern_index < outer_loop_input_size; - ++pattern_index) { - auto pattern_variables_indices = var_indices_[pattern_index]; - for (auto variables_index : pattern_variables_indices) { - const auto &solution = sub_problem_solution[variables_index]; - outerLoopPatternsValues[pattern_index] += solution; - if (solution > criterion_count_threshold) - // 1h of no supplied energy - outerLoopCriterions[pattern_index] += subproblem_weight; + std::vector &criteria, + std::vector &patterns_values) { + auto criteria_input_size = var_indices_.size(); // num of patterns + criteria.resize(criteria_input_size, 0.); + patterns_values.resize(criteria_input_size, 0.); + + double criterion_count_threshold = + criterion_input_data_.CriterionCountThreshold(); + + for (int pattern_index(0); pattern_index < criteria_input_size; + ++pattern_index) { + auto pattern_variables_indices = var_indices_[pattern_index]; + for (auto variables_index: pattern_variables_indices) { + const auto &solution = sub_problem_solution[variables_index]; + patterns_values[pattern_index] += solution; + if (solution > criterion_count_threshold) + // 1h of no supplied energy + criteria[pattern_index] += subproblem_weight; + } } - } } void CriterionComputation::SearchVariables( const std::vector &variables) { Benders::Criterion::VariablesGroup variablesGroup( - variables, outer_loop_input_data_.OuterLoopData()); + variables, criterion_input_data_.Criteria()); var_indices_ = variablesGroup.Indices(); } -const OuterLoopInputData &CriterionComputation::getOuterLoopInputData() const { - return outer_loop_input_data_; +const CriterionInputData &CriterionComputation::getCriterionInputData() const { + return criterion_input_data_; } std::vector> &CriterionComputation::getVarIndices() { return var_indices_; } CriterionComputation::CriterionComputation( - const OuterLoopInputData &outer_loop_input_data) - : outer_loop_input_data_(outer_loop_input_data) {} - + const CriterionInputData &criterion_input_data) + : criterion_input_data_(criterion_input_data) { +} } // namespace Benders::Criterion \ No newline at end of file diff --git a/src/cpp/benders/benders_core/OuterLoopInputDataReader.cpp b/src/cpp/benders/benders_core/CriterionInputDataReader.cpp similarity index 51% rename from src/cpp/benders/benders_core/OuterLoopInputDataReader.cpp rename to src/cpp/benders/benders_core/CriterionInputDataReader.cpp index cf58717c6..17cc1a029 100644 --- a/src/cpp/benders/benders_core/OuterLoopInputDataReader.cpp +++ b/src/cpp/benders/benders_core/CriterionInputDataReader.cpp @@ -1,4 +1,4 @@ -#include "include/antares-xpansion/benders/benders_core/OuterLoopInputDataReader.h" +#include "include/antares-xpansion/benders/benders_core/CriterionInputDataReader.h" #include @@ -10,86 +10,77 @@ using namespace Benders::Criterion; * prefix could be := PositiveUnsuppliedEnergy:: or something else necessarily * /!\ body could be := area name or equivalent or nothing */ -OuterLoopPattern::OuterLoopPattern(std::string prefix, - std::string body) +CriterionPattern::CriterionPattern(std::string prefix, std::string body) : prefix_(std::move(prefix)), body_(std::move(body)) {} /** * just do * just cat ;) */ -std::regex OuterLoopPattern::MakeRegex() const { +std::regex CriterionPattern::MakeRegex() const { auto pattern = "(^" + prefix_ + "area<" + body_ + ">" + ")"; return std::regex(pattern); } -const std::string &OuterLoopPattern::GetPrefix() const { return prefix_; } -void OuterLoopPattern::SetPrefix(const std::string &prefix) { +const std::string &CriterionPattern::GetPrefix() const { return prefix_; } +void CriterionPattern::SetPrefix(const std::string &prefix) { prefix_ = prefix; } -const std::string &OuterLoopPattern::GetBody() const { return body_; } +const std::string &CriterionPattern::GetBody() const { return body_; } -void OuterLoopPattern::SetBody(const std::string &body) { body_ = body; } +void CriterionPattern::SetBody(const std::string &body) { body_ = body; } -OuterLoopSingleInputData::OuterLoopSingleInputData(const std::string &prefix, +CriterionSingleInputData::CriterionSingleInputData(const std::string &prefix, const std::string &body, double criterion) - : outer_loop_pattern_(prefix, body), criterion_(criterion) {} + : pattern_(prefix, body), criterion_(criterion) {} -OuterLoopPattern OuterLoopSingleInputData::Pattern() const { - return outer_loop_pattern_; -} -double OuterLoopSingleInputData::Criterion() const { return criterion_; } +CriterionPattern CriterionSingleInputData::Pattern() const { return pattern_; } +double CriterionSingleInputData::Criterion() const { return criterion_; } -void OuterLoopSingleInputData::SetCriterion(double criterion) { +void CriterionSingleInputData::SetCriterion(double criterion) { criterion_ = criterion; } -void OuterLoopSingleInputData::ResetPattern(const std::string &prefix, +void CriterionSingleInputData::ResetPattern(const std::string &prefix, const std::string &body) { - outer_loop_pattern_.SetPrefix(prefix); - outer_loop_pattern_.SetBody(body); + pattern_.SetPrefix(prefix); + pattern_.SetBody(body); } -void OuterLoopInputData::AddSingleData(const OuterLoopSingleInputData &data) { - outer_loop_data_.push_back(data); +void CriterionInputData::AddSingleData(const CriterionSingleInputData &data) { + criterion_vector_.push_back(data); } -const std::vector &OuterLoopInputData::OuterLoopData() - const { - return outer_loop_data_; +const std::vector & +CriterionInputData::Criteria() const { + return criterion_vector_; } -std::vector OuterLoopInputData::PatternBodies() const { +std::vector CriterionInputData::PatternBodies() const { std::vector ret; - for (const auto &data : outer_loop_data_) { + for (const auto &data : criterion_vector_) { ret.push_back(data.Pattern().GetBody()); } return ret; } -std::string OuterLoopInputData::PatternsPrefix() const { +std::string CriterionInputData::PatternsPrefix() const { std::string ret(""); - if (!outer_loop_data_.empty()) { + if (!criterion_vector_.empty()) { ret = - StringManip::split(outer_loop_data_[0].Pattern().GetPrefix(), "::")[0]; + StringManip::split(criterion_vector_[0].Pattern().GetPrefix(), "::")[0]; } return ret; } -void OuterLoopInputData::SetStoppingThreshold( - double outer_loop_stopping_threshold) { - outer_loop_stopping_threshold_ = outer_loop_stopping_threshold; -} -double OuterLoopInputData::StoppingThreshold() const { - return outer_loop_stopping_threshold_; +void CriterionInputData::SetCriterionCountThreshold(double count_threshold) { + count_threshold_ = count_threshold; } -void OuterLoopInputData::SetCriterionCountThreshold( - double criterion_count_threshold) { - criterion_count_threshold_ = criterion_count_threshold; +double CriterionInputData::CriterionCountThreshold() const { + return count_threshold_; } -double OuterLoopInputData::CriterionCountThreshold() const { return criterion_count_threshold_; } -OuterLoopInputData OuterLoopInputFromYaml::Read( +OuterLoopCriterionInputData CriterionInputFromYaml::Read( const std::filesystem::path &input_file) { YAML::Node yaml_content; try { @@ -98,30 +89,36 @@ OuterLoopInputData OuterLoopInputFromYaml::Read( std::ostringstream err_msg; err_msg << "Could not read outer loop input file: " << input_file << "\n" << e.what(); - throw OuterLoopInputFileError( + throw CriterionInputFileError( PrefixMessage(LogUtils::LOGLEVEL::FATAL, "Outer Loop"), err_msg.str(), LOGLOCATION); } if (yaml_content.IsNull()) { std::ostringstream err_msg; err_msg << "outer loop input file is empty: " << input_file << "\n"; - throw OuterLoopInputFileIsEmpty( + throw CriterionInputFileIsEmpty( PrefixMessage(LogUtils::LOGLEVEL::FATAL, "Outer Loop"), err_msg.str(), LOGLOCATION); } - return yaml_content.as(); + return yaml_content.as(); +} + +double OuterLoopCriterionInputData::StoppingThreshold() const { + return stopping_threshold_; +} + +void OuterLoopCriterionInputData::setStoppingThreshold( + double stoppingThreshold) { + stopping_threshold_ = stoppingThreshold; } namespace YAML { template <> -struct convert { - static Node encode(const OuterLoopSingleInputData &rhs) { - return {}; - } - - static bool decode(const Node &pattern, OuterLoopSingleInputData &rhs) { +class convert { + public: + static bool decode(const Node &pattern, CriterionSingleInputData &rhs) { auto body = pattern["area"]; // specify line And OR #pattern @@ -130,9 +127,9 @@ struct convert { err_msg << PrefixMessage(LogUtils::LOGLEVEL::FATAL, "Outer Loop") << "Error could not read 'area' field in outer loop input file" << "\n"; - throw OuterLoopCouldNotReadAreaField(err_msg.str(), LOGLOCATION); + throw CouldNotReadAreaField(err_msg.str(), LOGLOCATION); } - auto criterion = pattern["criterion"]; + const auto criterion = pattern["criterion"]; if (criterion.IsNull()) { std::ostringstream err_msg; @@ -140,7 +137,7 @@ struct convert { << PrefixMessage(LogUtils::LOGLEVEL::FATAL, "Outer Loop") << "Error could not read 'criterion' field in outer loop input file" << "\n"; - throw OuterLoopCouldNotReadCriterionField(err_msg.str(), LOGLOCATION); + throw CouldNotReadCriterionField(err_msg.str(), LOGLOCATION); } rhs.SetCriterion(criterion.as()); @@ -149,28 +146,27 @@ struct convert { } }; template <> -struct convert { - static Node encode(const OuterLoopInputData &rhs) { - return {}; - } - - static void DecodePatterns(const Node &patterns, OuterLoopInputData &rhs) { +class convert { + public: + static void DecodePatterns(const Node &patterns, CriterionInputData &rhs) { if (!patterns.IsSequence()) { std::ostringstream err_msg; err_msg << "In outer loop input file 'patterns' should be an array." << "\n"; - throw OuterLoopInputPatternsShouldBeArray( + throw CriterionInputPatternsShouldBeArray( PrefixMessage(LogUtils::LOGLEVEL::FATAL, "Outer Loop"), err_msg.str(), LOGLOCATION); } for (const auto &pattern : patterns) { - rhs.AddSingleData(pattern.as()); + rhs.AddSingleData(pattern.as()); } } - static bool decode(const Node &node, OuterLoopInputData &rhs) { - rhs.SetStoppingThreshold(node["stopping_threshold"].as(1e-4)); + static bool decode(const Node &node, OuterLoopCriterionInputData &rhs) { + rhs.setStoppingThreshold(node["stopping_threshold"].as(1e-4)); + Benders::Criterion::CriterionInputData sub_rhs; + rhs.SetCriterionCountThreshold( node["criterion_count_threshold"].as(1)); @@ -180,11 +176,11 @@ struct convert { std::ostringstream err_msg; err_msg << "outer loop input file must contains at least one pattern." << "\n"; - throw OuterLoopInputFileNoPatternFound( + throw CriterionInputFileNoPatternFound( PrefixMessage(LogUtils::LOGLEVEL::FATAL, "Outer Loop"), err_msg.str(), LOGLOCATION); } return true; } }; -} // namespace YAML \ No newline at end of file +} // namespace YAML diff --git a/src/cpp/benders/benders_core/VariablesGroup.cpp b/src/cpp/benders/benders_core/VariablesGroup.cpp index b724d4a30..c359dbf3c 100644 --- a/src/cpp/benders/benders_core/VariablesGroup.cpp +++ b/src/cpp/benders/benders_core/VariablesGroup.cpp @@ -14,8 +14,8 @@ using namespace Benders::Criterion; VariablesGroup::VariablesGroup( const std::vector& all_variables, - const std::vector& outer_loop_single_input_data) - : all_variables_(all_variables), outer_loop_single_input_data_(outer_loop_single_input_data) { + const std::vector& criterion_single_input_data) + : all_variables_(all_variables), criterion_single_input_data_(criterion_single_input_data) { Search(); } @@ -24,11 +24,11 @@ std::vector> VariablesGroup::Indices() const { } void VariablesGroup::Search() { - indices_.assign(outer_loop_single_input_data_.size(), {}); + indices_.assign(criterion_single_input_data_.size(), {}); int var_index(0); for (const auto& variable : all_variables_) { int pattern_index(0); - for (const auto& single_input_data : outer_loop_single_input_data_) { + for (const auto& single_input_data : criterion_single_input_data_) { if (std::regex_search(variable, single_input_data.Pattern().MakeRegex())) { indices_[pattern_index].push_back(var_index); } diff --git a/src/cpp/benders/benders_core/common.cpp b/src/cpp/benders/benders_core/common.cpp index 6ff942024..4c50a8ec5 100644 --- a/src/cpp/benders/benders_core/common.cpp +++ b/src/cpp/benders/benders_core/common.cpp @@ -29,44 +29,8 @@ void usage(int argc) { } } -/*! - * \brief Build the input from the structure file - * - * Function to build the map linking each problem name to its variables and - *their id - * - * \param root : root of the structure file - * - * \param summary_name : name of the structure file - * - * \param coupling_map : empty map to increment - * - * \note The id in the coupling_map is that of the variable in the solver - *responsible for the creation of the structure file. - */ -CouplingMap build_input(const std::filesystem::path &structure_path) { - CouplingMap coupling_map; - std::ifstream summary(structure_path, std::ios::in); - if (!summary) { - std::cout << "Cannot open structure file " << structure_path << std::endl; - return coupling_map; - } - std::string line; - while (std::getline(summary, line)) { - std::stringstream buffer(line); - std::string problem_name; - std::string variable_name; - int variable_id; - buffer >> problem_name; - buffer >> variable_name; - buffer >> variable_id; - coupling_map[problem_name][variable_name] = variable_id; - } - summary.close(); - return coupling_map; -} Json::Value get_json_file_content(const std::filesystem::path &json_file) { std::ifstream input_file_l(json_file, std::ifstream::binary); diff --git a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersBase.h b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersBase.h index 296caa903..3f9be98ae 100644 --- a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersBase.h +++ b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersBase.h @@ -82,9 +82,13 @@ class BendersBase { } BendersBaseOptions Options() const { return _options; } virtual void free() = 0; - int GetBendersRunNumber() const { return _data.outer_loop_current_iteration_data.benders_num_run; } + + int GetBendersRunNumber() const { return _data.criteria_current_iteration_data.benders_num_run; } + CurrentIterationData GetCurrentIterationData() const; - OuterLoopCurrentIterationData GetOuterLoopData() const; + + CriteriaCurrentIterationData GetOuterLoopData() const; + std::vector GetOuterLoopCriterionAtBestBenders() const; virtual void init_data(); void init_data(double external_loop_lambda, double external_loop_lambda_min, double external_loop_lambda_max); @@ -95,14 +99,12 @@ class BendersBase { void UpdateOuterLoopSolution(); bool isExceptionRaised() const; - [[nodiscard]] std::filesystem::path OuterloopOptionsFile() const; void UpdateOverallCosts(); Logger _logger; Writer _writer; std::shared_ptr mathLoggerDriver_; - void setCriterionsComputation( - std::shared_ptr - criterionsComputation); + void setCriterionComputationInputs( + const Benders::Criterion::CriterionInputData &criterion_input_data); protected: bool exception_raised_ = false; @@ -118,7 +120,7 @@ class BendersBase { bool init_problems_ = true; bool free_problems_ = true; - std::vector> outer_loop_criterion_; + std::vector > criteria_vector_for_each_iteration_; bool is_bilevel_check_all_ = false; virtual void Run() = 0; @@ -223,9 +225,8 @@ class BendersBase { PlainData::SubProblemData &subproblem_data, const std::string &name, const std::shared_ptr &worker); - // TODO to be rethink - std::shared_ptr - criterions_computation_; + + Benders::Criterion::CriterionComputation criterion_computation_; /** * for the nth variable name, Subproblems shares the same prefix , only the suffix is different diff --git a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersMathLogger.h b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersMathLogger.h index c1a62c16f..a48a90fb8 100644 --- a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersMathLogger.h +++ b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersMathLogger.h @@ -134,7 +134,7 @@ struct MathLoggerExternalLoopSpecific : public MathLogger { explicit MathLoggerExternalLoopSpecific( const std::filesystem::path& file_path, const std::vector& headers, - T OuterLoopCurrentIterationData::*ptr) + T CriteriaCurrentIterationData::*ptr) : MathLogger(file_path), ptr_(ptr) { headers_.push_back("Outer loop"); headers_.push_back("Ite"); @@ -150,7 +150,7 @@ struct MathLoggerExternalLoopSpecific : public MathLogger { private: std::vector headers_; - T OuterLoopCurrentIterationData::*ptr_; + T CriteriaCurrentIterationData::*ptr_; }; class MathLoggerImplementation : public MathLoggerBehaviour { @@ -193,7 +193,7 @@ class MathLoggerDriver : public ILoggerXpansion { template void add_logger(const std::filesystem::path& file_path, const std::vector& headers, - T OuterLoopCurrentIterationData::*t); + T CriteriaCurrentIterationData::*t); void Print(const CurrentIterationData& data); virtual void PrintIterationSeparatorBegin() override; virtual void PrintIterationSeparatorEnd() override; @@ -206,7 +206,7 @@ class MathLoggerDriver : public ILoggerXpansion { template void MathLoggerDriver::add_logger(const std::filesystem::path& file_path, const std::vector& headers, - T OuterLoopCurrentIterationData::*t) { + T CriteriaCurrentIterationData::*t) { auto impl = std::make_shared>(file_path, headers, t); add_logger(std::make_shared(impl)); @@ -220,11 +220,11 @@ template void MathLoggerExternalLoopSpecific::Print( const CurrentIterationData& data) { LogsDestination().InsertDelimiter(); - LogsDestination() << data.outer_loop_current_iteration_data.benders_num_run; + LogsDestination() << data.criteria_current_iteration_data.benders_num_run; LogsDestination().InsertDelimiter(); LogsDestination() << data.it; LogsDestination().InsertDelimiter(); - for (const auto& t : data.outer_loop_current_iteration_data.*ptr_) { + for (const auto& t : data.criteria_current_iteration_data.*ptr_) { LogsDestination() << std::scientific << std::setprecision(10) << t; LogsDestination().InsertDelimiter(); } diff --git a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersStructsDatas.h b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersStructsDatas.h index d11659950..c193252ac 100644 --- a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersStructsDatas.h +++ b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/BendersStructsDatas.h @@ -5,16 +5,16 @@ #include "Worker.h" #include "common.h" -struct OuterLoopCurrentIterationData{ +struct CriteriaCurrentIterationData { int benders_num_run = 0; - std::vector outer_loop_criterion = {}; - std::vector outer_loop_patterns_values = {}; + std::vector criteria = {}; + std::vector patterns_values = {}; double max_criterion = 0.; double max_criterion_best_it = 0.; double outer_loop_bilevel_best_ub = +1e20; - double external_loop_lambda = 0.; - double external_loop_lambda_min = 0.; - double external_loop_lambda_max = 0.; + double lambda = 0.; + double lambda_min = 0.; + double lambda_max = 0.; std::string max_criterion_area = "N/A"; std::string max_criterion_area_best_it = "N/A"; }; @@ -53,7 +53,7 @@ struct CurrentIterationData { int min_simplexiter; int max_simplexiter; // ugly - OuterLoopCurrentIterationData outer_loop_current_iteration_data; + CriteriaCurrentIterationData criteria_current_iteration_data; }; // /*! \struct to store benders cuts data diff --git a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CouplingMapGenerator.h b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CouplingMapGenerator.h new file mode 100644 index 000000000..3a71a73c2 --- /dev/null +++ b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CouplingMapGenerator.h @@ -0,0 +1,9 @@ +#pragma once +#include "antares-xpansion/xpansion_interfaces/ILogger.h" +#include "common.h" +class CouplingMapGenerator { + public: + static CouplingMap BuildInput(const std::filesystem::path& structure_path, + std::shared_ptr logger, + const std::string& context = "Benders"); +}; diff --git a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CriterionComputation.h b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CriterionComputation.h index aa601d23c..6e4155b10 100644 --- a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CriterionComputation.h +++ b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CriterionComputation.h @@ -1,22 +1,27 @@ #pragma once -#include "OuterLoopInputDataReader.h" +#include "CriterionInputDataReader.h" #include "VariablesGroup.h" namespace Benders::Criterion { class CriterionComputation { public: + /** + * @brief default constructor + */ + explicit CriterionComputation() = default; + /** * @brief Constructs a CriterionComputation object. * * This constructor initializes the CriterionComputation instance with the - * provided outer loop input data. + * provided criterion input data. * - * @param outer_loop_input_data The input data to be used for criterion + * @param criterion_input_data The input data to be used for criterion * computation. */ explicit CriterionComputation( - const OuterLoopInputData &outer_loop_input_data); + const CriterionInputData &criterion_input_data); /** * @brief Searches for relevant variables based on the provided variable @@ -31,25 +36,25 @@ class CriterionComputation { void SearchVariables(const std::vector &variables); /** - * @brief Computes the outer loop criterion based on subproblem solutions. + * @brief Computes the criteria based on subproblem solutions. * - * This method calculates the outer loop criteria and pattern values + * This method calculates the criterion criteria and pattern values * based on the provided subproblem weight and solution. It updates the - * outerLoopCriterions and outerLoopPatternsValues vectors accordingly. + * criteria and patterns values vectors accordingly. * * @param subproblem_weight The weight of the subproblem affecting the * criteria. * @param sub_problem_solution A vector containing the solutions of the * subproblem. - * @param outerLoopCriterions A reference to a vector where the computed + * @param criteria A reference to a vector where the computed * criteria will be stored. - * @param outerLoopPatternsValues A reference to a vector where the computed + * @param patterns_values A reference to a vector where the computed * pattern values will be stored. */ - void ComputeOuterLoopCriterion( + void ComputeCriterion( double subproblem_weight, const std::vector &sub_problem_solution, - std::vector &outerLoopCriterions, - std::vector &outerLoopPatternsValues); + std::vector &criteria, + std::vector &patterns_values); /** * @brief Retrieves the variable indices. @@ -62,17 +67,17 @@ class CriterionComputation { std::vector> &getVarIndices(); /** - * @brief Retrieves the outer loop input data. + * @brief Retrieves the criterion input data. * - * This method returns a constant reference to the outer loop input data + * This method returns a constant reference to the criterion input data * associated with this CriterionComputation instance. * - * @return A constant reference to the OuterLoopInputData object. + * @return A constant reference to the CriterionInputData object. */ - const OuterLoopInputData &getOuterLoopInputData() const; + const CriterionInputData &getCriterionInputData() const; - private: - std::vector> var_indices_; - const OuterLoopInputData outer_loop_input_data_; +private: + std::vector> var_indices_ = {}; + CriterionInputData criterion_input_data_; }; } // namespace Benders::Criterion \ No newline at end of file diff --git a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/OuterLoopInputDataReader.h b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CriterionInputDataReader.h similarity index 51% rename from src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/OuterLoopInputDataReader.h rename to src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CriterionInputDataReader.h index fcda0f967..df576689a 100644 --- a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/OuterLoopInputDataReader.h +++ b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/CriterionInputDataReader.h @@ -11,40 +11,40 @@ namespace Benders::Criterion { static constexpr const char *const PositiveUnsuppliedEnergy = "PositiveUnsuppliedEnergy::"; -class OuterLoopInputFileError +class CriterionInputFileError : public LogUtils::XpansionError { using LogUtils::XpansionError::XpansionError; }; -class OuterLoopInputFileIsEmpty +class CriterionInputFileIsEmpty : public LogUtils::XpansionError { using LogUtils::XpansionError::XpansionError; }; -class OuterLoopInputFileNoPatternFound +class CriterionInputFileNoPatternFound : public LogUtils::XpansionError { using LogUtils::XpansionError::XpansionError; }; -class OuterLoopInputPatternsShouldBeArray +class CriterionInputPatternsShouldBeArray : public LogUtils::XpansionError { using LogUtils::XpansionError::XpansionError; }; -class OuterLoopCouldNotReadAreaField - : public LogUtils::XpansionError { - using LogUtils::XpansionError::XpansionError; +class CouldNotReadAreaField + : public LogUtils::XpansionError { + using LogUtils::XpansionError::XpansionError; }; -class OuterLoopCouldNotReadCriterionField - : public LogUtils::XpansionError { - using LogUtils::XpansionError::XpansionError; +class CouldNotReadCriterionField + : public LogUtils::XpansionError { + using LogUtils::XpansionError::XpansionError; }; /// @brief lovely class -class OuterLoopPattern { +class CriterionPattern { public: - explicit OuterLoopPattern(std::string prefix, std::string body); - OuterLoopPattern() = default; + explicit CriterionPattern(std::string prefix, std::string body); + CriterionPattern() = default; [[nodiscard]] std::regex MakeRegex() const; [[nodiscard]] const std::string &GetPrefix() const; void SetPrefix(const std::string &prefix); @@ -57,62 +57,73 @@ class OuterLoopPattern { }; -/// @brief holds the pattern and the criterion of the outer loop -class OuterLoopSingleInputData { +/// @brief holds the pattern and the criterion +class CriterionSingleInputData { public: - OuterLoopSingleInputData() = default; + CriterionSingleInputData() = default; /// @brief constructor /// @param prefix the prefix in the variable's name /// @param body any string that could be in the variable's name /// @param criterion the criterion that should be satisfied - OuterLoopSingleInputData(const std::string &prefix, const std::string &body, + CriterionSingleInputData(const std::string &prefix, const std::string &body, double criterion); - [[nodiscard]] OuterLoopPattern Pattern() const; + [[nodiscard]] CriterionPattern Pattern() const; [[nodiscard]] double Criterion() const; void SetCriterion(double criterion); void ResetPattern(const std::string &prefix, const std::string &body); private: - OuterLoopPattern outer_loop_pattern_; + CriterionPattern pattern_; double criterion_ = 0; }; /// @brief this class contains all data read from user input file -class OuterLoopInputData { +class CriterionInputData { public: - OuterLoopInputData() = default; + CriterionInputData() = default; + + [[nodiscard]] const std::vector &Criteria() + const; - [[nodiscard]] const std::vector &OuterLoopData() - const; [[nodiscard]] std::vector PatternBodies() const; [[nodiscard]] std::string PatternsPrefix() const; - void SetStoppingThreshold(double outer_loop_stopping_threshold); - [[nodiscard]] double StoppingThreshold() const; - void SetCriterionCountThreshold(double criterion_count_threshold); + void SetCriterionCountThreshold(double count_threshold); [[nodiscard]] double CriterionCountThreshold() const; - void AddSingleData(const OuterLoopSingleInputData &data); + void AddSingleData(const CriterionSingleInputData &data); + + private: + std::vector criterion_vector_; + double count_threshold_ = 1; +}; + +/// @brief this class contains all data read from user input file +class OuterLoopCriterionInputData : public CriterionInputData { + public: + OuterLoopCriterionInputData() = default; + [[nodiscard]] double StoppingThreshold() const; + void setStoppingThreshold(double stoppingThreshold); private: - double outer_loop_stopping_threshold_ = 1e-4; - std::vector outer_loop_data_; - double criterion_count_threshold_ = 1; + double stopping_threshold_ = 1e-4; }; -/// @brief Abstract /*** -class IOuterLoopInputDataReader { +/// @brief Abstract +class ICriterionInputDataReader { public: - virtual OuterLoopInputData Read(const std::filesystem::path &input_file) = 0; - virtual ~IOuterLoopInputDataReader() = default; + virtual OuterLoopCriterionInputData Read( + const std::filesystem::path &input_file) = 0; + virtual ~ICriterionInputDataReader() = default; }; -class OuterLoopInputFromYaml : public IOuterLoopInputDataReader { +class CriterionInputFromYaml : public ICriterionInputDataReader { public: - OuterLoopInputFromYaml() = default; - OuterLoopInputData Read(const std::filesystem::path &input_file) override; + CriterionInputFromYaml() = default; + OuterLoopCriterionInputData Read( + const std::filesystem::path &input_file) override; private: - OuterLoopInputData outerLoopInputData_; + CriterionInputData criterion_input_data_; }; } // namespace Benders::Criterion \ No newline at end of file diff --git a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/SubproblemCut.h b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/SubproblemCut.h index ddcf43a87..5a0adb214 100644 --- a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/SubproblemCut.h +++ b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/SubproblemCut.h @@ -9,9 +9,9 @@ namespace PlainData { struct SubProblemData { double subproblem_cost; Point var_name_and_subgradient; - std::vector outer_loop_criterions; + std::vector criteria; // no-supplied energy - std::vector outer_loop_patterns_values; + std::vector patterns_values; double single_subpb_costs_under_approx; double subproblem_timer; int simplex_iter; @@ -21,7 +21,7 @@ struct SubProblemData { void serialize(Archive &ar, const unsigned int version) { ar & subproblem_cost; ar & var_name_and_subgradient; - ar & outer_loop_criterions; + ar & criteria; ar & single_subpb_costs_under_approx; ar & subproblem_timer; ar & simplex_iter; diff --git a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/VariablesGroup.h b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/VariablesGroup.h index 4f82153d5..69a3f5444 100644 --- a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/VariablesGroup.h +++ b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/VariablesGroup.h @@ -3,18 +3,20 @@ #include #include -#include "OuterLoopInputDataReader.h" +#include "CriterionInputDataReader.h" namespace Benders::Criterion { class VariablesGroup { public: explicit VariablesGroup(const std::vector& all_variables, - const std::vector& outer_loop_single_input_data); + const std::vector& + criterion_single_input_data); + [[nodiscard]] std::vector> Indices() const; private: void Search(); const std::vector& all_variables_; - const std::vector& outer_loop_single_input_data_; + const std::vector &criterion_single_input_data_; std::vector> indices_; }; } // namespace Benders::Criterion \ No newline at end of file diff --git a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/common.h b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/common.h index f2799800f..795b4f15d 100644 --- a/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/common.h +++ b/src/cpp/benders/benders_core/include/antares-xpansion/benders/benders_core/common.h @@ -191,5 +191,4 @@ struct BendersBaseOptions : public BaseOptions { }; void usage(int argc); -CouplingMap build_input(const std::filesystem::path &structure_path); Json::Value get_json_file_content(const std::filesystem::path &json_file); diff --git a/src/cpp/benders/benders_mpi/BendersMPI.cpp b/src/cpp/benders/benders_mpi/BendersMPI.cpp index 90f962f59..550bf1beb 100644 --- a/src/cpp/benders/benders_mpi/BendersMPI.cpp +++ b/src/cpp/benders/benders_mpi/BendersMPI.cpp @@ -50,7 +50,7 @@ void BendersMpi::BroadCastVariablesIndices() { if (_world.rank() == rank_0) { SetSubproblemsVariablesIndices(); } - BroadCast(criterions_computation_->getVarIndices(), rank_0); + BroadCast(criterion_computation_.getVarIndices(), rank_0); } void BendersMpi::BuildMasterProblem() { @@ -159,10 +159,10 @@ void BendersMpi::GatherCuts(const SubProblemDataMap &subproblem_data_map, // only rank_0 receive non-emtpy gathered_subproblem_map master_build_cuts(gathered_subproblem_map); - ComputeSubproblemsContributionToOuterLoopCriterion(subproblem_data_map); + ComputeSubproblemsContributionToCriteria(subproblem_data_map); if (_world.rank() == rank_0) { - outer_loop_criterion_.push_back( - _data.outer_loop_current_iteration_data.outer_loop_criterion); + criteria_vector_for_each_iteration_.push_back( + _data.criteria_current_iteration_data.criteria); UpdateMaxCriterionArea(); } } @@ -176,54 +176,54 @@ void BendersMpi::SolveSubproblem( std::vector solution; worker->get_solution(solution); - criterions_computation_->ComputeOuterLoopCriterion( - SubproblemWeight(_data.nsubproblem, name), solution, - subproblem_data.outer_loop_criterions, - subproblem_data.outer_loop_patterns_values); + criterion_computation_.ComputeCriterion( + SubproblemWeight(_data.nsubproblem, name), solution, + subproblem_data.criteria, + subproblem_data.patterns_values); } void BendersMpi::UpdateMaxCriterionArea() { - auto criterions_begin = - _data.outer_loop_current_iteration_data.outer_loop_criterion.cbegin(); - auto criterions_end = - _data.outer_loop_current_iteration_data.outer_loop_criterion.cend(); - auto max_criterion_it = std::max_element(criterions_begin, criterions_end); - if (max_criterion_it != criterions_end) { - _data.outer_loop_current_iteration_data.max_criterion = *max_criterion_it; + auto criteria_begin = + _data.criteria_current_iteration_data.criteria.cbegin(); + auto criteria_end = + _data.criteria_current_iteration_data.criteria.cend(); + auto max_criterion_it = std::max_element(criteria_begin, criteria_end); + if (max_criterion_it != criteria_end) { + _data.criteria_current_iteration_data.max_criterion = *max_criterion_it; auto max_criterion_index = - std::distance(criterions_begin, max_criterion_it); - _data.outer_loop_current_iteration_data.max_criterion_area = - criterions_computation_->getOuterLoopInputData() - .OuterLoopData()[max_criterion_index] - .Pattern() - .GetBody(); + std::distance(criteria_begin, max_criterion_it); + _data.criteria_current_iteration_data.max_criterion_area = + criterion_computation_.getCriterionInputData() + .Criteria()[max_criterion_index] + .Pattern() + .GetBody(); } } -void BendersMpi::ComputeSubproblemsContributionToOuterLoopCriterion( - const SubProblemDataMap &subproblem_data_map) { - const auto vars_size = criterions_computation_->getVarIndices().size(); - std::vector outer_loop_criterion_per_sub_problem_per_pattern( - vars_size, {}); - _data.outer_loop_current_iteration_data.outer_loop_criterion.resize(vars_size, - 0.); - std::vector outer_loop_patterns_values_per_sub_problem_per_pattern( - vars_size, {}); - _data.outer_loop_current_iteration_data.outer_loop_patterns_values.resize( - vars_size, 0.); +void BendersMpi::ComputeSubproblemsContributionToCriteria( + const SubProblemDataMap &subproblem_data_map) { + const auto vars_size = criterion_computation_.getVarIndices().size(); + std::vector criteria_per_sub_problem_per_pattern( + vars_size, {}); + _data.criteria_current_iteration_data.criteria.resize(vars_size, + 0.); + std::vector patterns_values_per_sub_problem_per_pattern( + vars_size, {}); + _data.criteria_current_iteration_data.patterns_values.resize( + vars_size, 0.); for (const auto &[subproblem_name, subproblem_data] : subproblem_data_map) { - AddVectors(outer_loop_criterion_per_sub_problem_per_pattern, - subproblem_data.outer_loop_criterions); - AddVectors(outer_loop_patterns_values_per_sub_problem_per_pattern, - subproblem_data.outer_loop_patterns_values); + AddVectors(criteria_per_sub_problem_per_pattern, + subproblem_data.criteria); + AddVectors(patterns_values_per_sub_problem_per_pattern, + subproblem_data.patterns_values); } - Reduce(outer_loop_criterion_per_sub_problem_per_pattern, - _data.outer_loop_current_iteration_data.outer_loop_criterion, + Reduce(criteria_per_sub_problem_per_pattern, + _data.criteria_current_iteration_data.criteria, std::plus(), rank_0); - Reduce(outer_loop_patterns_values_per_sub_problem_per_pattern, - _data.outer_loop_current_iteration_data.outer_loop_patterns_values, + Reduce(patterns_values_per_sub_problem_per_pattern, + _data.criteria_current_iteration_data.patterns_values, std::plus(), rank_0); } diff --git a/src/cpp/benders/benders_mpi/BendersMpiOuterLoop.cpp b/src/cpp/benders/benders_mpi/BendersMpiOuterLoop.cpp index e532f45b2..95c07b24a 100644 --- a/src/cpp/benders/benders_mpi/BendersMpiOuterLoop.cpp +++ b/src/cpp/benders/benders_mpi/BendersMpiOuterLoop.cpp @@ -11,7 +11,7 @@ BendersMpiOuterLoop::BendersMpiOuterLoop( void BendersMpiOuterLoop::launch() { - ++_data.outer_loop_current_iteration_data.benders_num_run; + ++_data.criteria_current_iteration_data.benders_num_run; BendersMpi::launch(); } } // namespace Outerloop \ No newline at end of file diff --git a/src/cpp/benders/benders_mpi/OuterLoopBenders.cpp b/src/cpp/benders/benders_mpi/OuterLoopBenders.cpp index e86211e34..f918e2903 100644 --- a/src/cpp/benders/benders_mpi/OuterLoopBenders.cpp +++ b/src/cpp/benders/benders_mpi/OuterLoopBenders.cpp @@ -2,7 +2,7 @@ namespace Outerloop { OuterLoopBenders::OuterLoopBenders( - const std::vector& + const std::vector& outer_loop_data, std::shared_ptr master_updater, std::shared_ptr cuts_manager, pBendersBase benders, @@ -115,7 +115,7 @@ void OuterLoopBenders::OuterLoopBilevelChecks() { const auto& x_cut = benders_->GetCurrentIterationData().x_cut; const auto& external_loop_lambda = benders_->GetCurrentIterationData() - .outer_loop_current_iteration_data.external_loop_lambda; + .criteria_current_iteration_data.lambda; if (outer_loop_biLevel_.Update_bilevel_data_if_feasible( x_cut, benders_->GetOuterLoopCriterionAtBestBenders() /*/!\ must be at best it*/ diff --git a/src/cpp/benders/benders_mpi/include/antares-xpansion/benders/benders_mpi/BendersMPI.h b/src/cpp/benders/benders_mpi/include/antares-xpansion/benders/benders_mpi/BendersMPI.h index 05da35767..b76183b8f 100644 --- a/src/cpp/benders/benders_mpi/include/antares-xpansion/benders/benders_mpi/BendersMPI.h +++ b/src/cpp/benders/benders_mpi/include/antares-xpansion/benders/benders_mpi/BendersMPI.h @@ -90,7 +90,7 @@ class BendersMpi : public BendersBase { virtual void GatherCuts(const SubProblemDataMap &subproblem_data_map, const Timer &walltime); void BroadCastVariablesIndices(); - virtual void ComputeSubproblemsContributionToOuterLoopCriterion( + virtual void ComputeSubproblemsContributionToCriteria( const SubProblemDataMap &subproblem_data_map); void SolveSubproblem( SubProblemDataMap &subproblem_data_map, diff --git a/src/cpp/benders/benders_mpi/include/antares-xpansion/benders/benders_mpi/OuterLoopBenders.h b/src/cpp/benders/benders_mpi/include/antares-xpansion/benders/benders_mpi/OuterLoopBenders.h index 9747f551e..19381674b 100644 --- a/src/cpp/benders/benders_mpi/include/antares-xpansion/benders/benders_mpi/OuterLoopBenders.h +++ b/src/cpp/benders/benders_mpi/include/antares-xpansion/benders/benders_mpi/OuterLoopBenders.h @@ -17,7 +17,7 @@ class CriterionCouldNotBeSatisfied class OuterLoopBenders : public OuterLoop { public: explicit OuterLoopBenders( - const std::vector& + const std::vector& outer_loop_data, std::shared_ptr master_updater, std::shared_ptr cuts_manager, pBendersBase benders, diff --git a/src/cpp/benders/factories/BendersFactory.cpp b/src/cpp/benders/factories/BendersFactory.cpp index b416dbe03..17f00b5c8 100644 --- a/src/cpp/benders/factories/BendersFactory.cpp +++ b/src/cpp/benders/factories/BendersFactory.cpp @@ -4,6 +4,7 @@ #include #include "antares-xpansion/benders/benders_by_batch/BendersByBatch.h" +#include "antares-xpansion/benders/benders_core/CouplingMapGenerator.h" #include "antares-xpansion/benders/benders_core/MasterUpdate.h" #include "antares-xpansion/benders/benders_core/StartUp.h" #include "antares-xpansion/benders/benders_mpi/BendersMpiOuterLoop.h" @@ -12,19 +13,18 @@ #include "antares-xpansion/benders/factories/WriterFactories.h" #include "antares-xpansion/helpers/AreaParser.h" #include "antares-xpansion/helpers/Timer.h" -#include "antares-xpansion/xpansion_interfaces/ILogger.h" #include "antares-xpansion/xpansion_interfaces/LogUtils.h" BENDERSMETHOD DeduceBendersMethod(size_t coupling_map_size, size_t batch_size, - bool external_loop) { + bool outer_loop) { if (batch_size == 0 || batch_size == coupling_map_size - 1) { - if (external_loop) { + if (outer_loop) { return BENDERSMETHOD::BENDERS_OUTERLOOP; } else { return BENDERSMETHOD::BENDERS; } } else { - if (external_loop) { + if (outer_loop) { return BENDERSMETHOD::BENDERS_BY_BATCH_OUTERLOOP; } else { return BENDERSMETHOD::BENDERS_BY_BATCH; @@ -32,82 +32,101 @@ BENDERSMETHOD DeduceBendersMethod(size_t coupling_map_size, size_t batch_size, } } -pBendersBase BendersMainFactory::PrepareForExecution(bool external_loop) { - pBendersBase benders; - std::shared_ptr math_log_driver; - +void BendersMainFactory::PrepareForExecution(bool outer_loop) { BendersBaseOptions benders_options(options_.get_benders_options()); - benders_options.EXTERNAL_LOOP_OPTIONS.DO_OUTER_LOOP = external_loop; + benders_options.EXTERNAL_LOOP_OPTIONS.DO_OUTER_LOOP = outer_loop; + SetupLoggerAndOutputWriter(benders_options); - const auto coupling_map = build_input(benders_options.STRUCTURE_FILE); + const auto coupling_map = CouplingMapGenerator::BuildInput( + benders_options.STRUCTURE_FILE, + std::make_shared(benders_loggers_), "Benders"); - method_ = DeduceBendersMethod(coupling_map.size(), options_.BATCH_SIZE, - external_loop); + method_ = + DeduceBendersMethod(coupling_map.size(), options_.BATCH_SIZE, outer_loop); context_ = bendersmethod_to_string(method_); - auto benders_log_console = benders_options.LOG_LEVEL > 0; + criterion_input_holder_ = ProcessCriterionInput(); + if (pworld_->rank() == 0) { - auto logger_factory = - FileAndStdoutLoggerFactory(LogReportsName(), benders_log_console); - logger_ = logger_factory.get_logger(); - math_log_driver = BuildMathLogger(benders_log_console); - writer_ = build_json_writer(options_.JSON_FILE, options_.RESUME); if (Benders::StartUp startup; - startup.StudyAlreadyAchievedCriterion(options_, writer_, logger_)) - return nullptr; - } else { - logger_ = build_void_logger(); - writer_ = build_void_writer(); - math_log_driver = MathLoggerFactory::get_void_logger(); + startup.StudyAlreadyAchievedCriterion(options_, writer_, logger_)) { + return; + } + if (!isCriterionListEmpty()) { + AddCriterionOutputs(); + } } - benders_loggers_.AddLogger(logger_); - benders_loggers_.AddLogger(math_log_driver); - auto outer_loop_input_data = ProcessCriterionInput(); - criterion_computation_ = - std::make_shared( - outer_loop_input_data); - - if (pworld_->rank() == 0 && !outer_loop_input_data.OuterLoopData().empty()) { - AddCriterionOutput(math_log_driver); + ConfigureBenders(benders_options, coupling_map); + ConfigureSolverLog(); +} + +void BendersMainFactory::ConfigureSolverLog() { + if (options_.LOG_LEVEL > 1) { + auto solver_log = std::filesystem::path(options_.OUTPUTROOT) / + (std::string("solver_log_proc_") + + std::to_string(pworld_->rank()) + ".txt"); + + benders_->set_solver_log_file(solver_log); } +} +void BendersMainFactory::ConfigureBenders( + const BendersBaseOptions& benders_options, + const CouplingMap& coupling_map) { switch (method_) { case BENDERSMETHOD::BENDERS: - benders = std::make_shared(benders_options, logger_, writer_, - *penv_, *pworld_, math_log_driver); + benders_ = + std::make_shared(benders_options, logger_, writer_, + *penv_, *pworld_, math_log_driver_); break; case BENDERSMETHOD::BENDERS_OUTERLOOP: - benders = std::make_shared( - benders_options, logger_, writer_, *penv_, *pworld_, math_log_driver); + benders_ = std::make_shared( + benders_options, logger_, writer_, *penv_, *pworld_, + math_log_driver_); break; case BENDERSMETHOD::BENDERS_BY_BATCH: case BENDERSMETHOD::BENDERS_BY_BATCH_OUTERLOOP: - benders = std::make_shared( - benders_options, logger_, writer_, *penv_, *pworld_, math_log_driver); + benders_ = + std::make_shared(benders_options, logger_, writer_, + *penv_, *pworld_, math_log_driver_); break; } - benders->set_input_map(coupling_map); - std::ostringstream oss_l = start_message(options_, benders->BendersName()); - oss_l << std::endl; - benders_loggers_.display_message(oss_l.str()); - - if (benders_options.LOG_LEVEL > 1) { - auto solver_log = std::filesystem::path(options_.OUTPUTROOT) / - (std::string("solver_log_proc_") + - std::to_string(pworld_->rank()) + ".txt"); + benders_->set_input_map(coupling_map); + benders_->setCriterionComputationInputs(std::visit( + [](auto&& the_variant) { + return static_cast(the_variant); + }, + criterion_input_holder_)); +} - benders->set_solver_log_file(solver_log); +void BendersMainFactory::SetupLoggerAndOutputWriter( + const BendersBaseOptions& benders_options) { + auto benders_log_console = benders_options.LOG_LEVEL > 0; + if (pworld_->rank() == 0) { + auto logger_factory = + FileAndStdoutLoggerFactory(LogReportsName(), benders_log_console); + logger_ = logger_factory.get_logger(); + math_log_driver_ = BuildMathLogger(benders_log_console); + writer_ = build_json_writer(options_.JSON_FILE, options_.RESUME); + } else { + logger_ = build_void_logger(); + writer_ = build_void_writer(); + math_log_driver_ = MathLoggerFactory::get_void_logger(); } - + benders_loggers_.AddLogger(logger_); + benders_loggers_.AddLogger(math_log_driver_); writer_->write_log_level(options_.LOG_LEVEL); writer_->write_master_name(options_.MASTER_NAME); writer_->write_solver_name(options_.SOLVER_NAME); - benders->setCriterionsComputation(criterion_computation_); +} - return benders; +bool BendersMainFactory::isCriterionListEmpty() const { + return std::visit( + [](auto &&the_variant) { return the_variant.Criteria().empty(); }, + criterion_input_holder_); } std::shared_ptr BendersMainFactory::BuildMathLogger( @@ -123,30 +142,32 @@ std::shared_ptr BendersMainFactory::BuildMathLogger( return math_log_driver; } -void BendersMainFactory::AddCriterionOutput( - std::shared_ptr math_log_driver) { +void BendersMainFactory::AddCriterionOutputs() { const std::filesystem::path output_root(options_.OUTPUTROOT); const auto& headers = - criterion_computation_->getOuterLoopInputData().PatternBodies(); - math_log_driver->add_logger( + std::visit([](auto&& the_variant) { return the_variant.PatternBodies(); }, + criterion_input_holder_); + math_log_driver_->add_logger( output_root / LOLD_FILE, headers, - &OuterLoopCurrentIterationData::outer_loop_criterion); + &CriteriaCurrentIterationData::criteria); - positive_unsupplied_file_ = - criterion_computation_->getOuterLoopInputData().PatternsPrefix() + ".txt"; - math_log_driver->add_logger( + positive_unsupplied_file_ = std::visit( + [](auto&& the_variant) { return the_variant.PatternsPrefix() + ".txt"; }, + criterion_input_holder_); + math_log_driver_->add_logger( output_root / positive_unsupplied_file_, headers, - &OuterLoopCurrentIterationData::outer_loop_patterns_values); + &CriteriaCurrentIterationData::patterns_values); } int BendersMainFactory::RunBenders() { try { - auto benders = PrepareForExecution(false); - if (benders) { - benders->launch(); - EndMessage(benders->execution_time()); + PrepareForExecution(false); + if (benders_) { + StartMessage(); + benders_->launch(); + EndMessage(benders_->execution_time()); } } catch (std::exception& e) { @@ -163,6 +184,14 @@ int BendersMainFactory::RunBenders() { return 0; } +void BendersMainFactory::StartMessage() { + std::ostringstream oss_l; + oss_l << "Starting " << context_ << std::endl; + options_.print(oss_l); + oss_l << std::endl; + benders_loggers_.display_message(oss_l.str()); +} + void BendersMainFactory::EndMessage(const double execution_time) { std::ostringstream str; str << "Optimization results available in : " << options_.JSON_FILE @@ -172,13 +201,13 @@ void BendersMainFactory::EndMessage(const double execution_time) { str.str(""); - str << bendersmethod_to_string(method_) << " ran in " << execution_time - << " s" << std::endl; + str << context_ << " ran in " << execution_time << " s" << std::endl; benders_loggers_.display_message(str.str(), LogUtils::LOGLEVEL::INFO, context_); } -Benders::Criterion::OuterLoopInputData +std::variant BendersMainFactory::ProcessCriterionInput() { const auto fpath = std::filesystem::path(options_.INPUTROOT) / options_.OUTER_LOOP_OPTION_FILE; @@ -186,7 +215,7 @@ BendersMainFactory::ProcessCriterionInput() { if ((method_ == BENDERSMETHOD::BENDERS_OUTERLOOP || method_ == BENDERSMETHOD::BENDERS_BY_BATCH_OUTERLOOP) && std::filesystem::exists(fpath)) { - return Benders::Criterion::OuterLoopInputFromYaml().Read(fpath); + return Benders::Criterion::CriterionInputFromYaml().Read(fpath); } // else compute criterion for all areas! else { @@ -194,14 +223,14 @@ BendersMainFactory::ProcessCriterionInput() { } } -Benders::Criterion::OuterLoopInputData +Benders::Criterion::CriterionInputData BendersMainFactory::BuildPatternsUsingAreaFile() { std::set unique_areas = ReadAreaFile(); - Benders::Criterion::OuterLoopInputData ret; + Benders::Criterion::CriterionInputData ret; ret.SetCriterionCountThreshold(1); for (const auto& area : unique_areas) { - Benders::Criterion::OuterLoopSingleInputData singleInputData( + Benders::Criterion::CriterionSingleInputData singleInputData( Benders::Criterion::PositiveUnsuppliedEnergy, area, 1); ret.AddSingleData(singleInputData); } @@ -231,20 +260,21 @@ std::set BendersMainFactory::ReadAreaFile() { int BendersMainFactory::RunExternalLoop() { try { - auto benders = PrepareForExecution(true); + PrepareForExecution(true); double tau = 0.5; - - const auto& criterion_input_data = - criterion_computation_->getOuterLoopInputData(); + const auto& outer_loop_inputs = + std::get( + criterion_input_holder_); std::shared_ptr master_updater = std::make_shared( - benders, tau, criterion_input_data.StoppingThreshold()); + benders_, tau, outer_loop_inputs.StoppingThreshold()); std::shared_ptr cuts_manager = std::make_shared(); - Outerloop::OuterLoopBenders ext_loop(criterion_input_data.OuterLoopData(), - master_updater, cuts_manager, benders, + Outerloop::OuterLoopBenders ext_loop(outer_loop_inputs.Criteria(), + master_updater, cuts_manager, benders_, *pworld_); + StartMessage(); ext_loop.Run(); EndMessage(ext_loop.Runtime()); diff --git a/src/cpp/benders/factories/LoggerFactories.cpp b/src/cpp/benders/factories/LoggerFactories.cpp index 89d40fb3e..747f9388c 100644 --- a/src/cpp/benders/factories/LoggerFactories.cpp +++ b/src/cpp/benders/factories/LoggerFactories.cpp @@ -8,11 +8,3 @@ Logger build_void_logger() { Logger logger = std::make_shared(); return logger; } - -std::ostringstream start_message(const SimulationOptions &options, - const std::string &benders_type) { - std::ostringstream oss_l; - oss_l << "starting " << benders_type << std::endl; - options.print(oss_l); - return oss_l; -} diff --git a/src/cpp/benders/factories/include/antares-xpansion/benders/factories/BendersFactory.h b/src/cpp/benders/factories/include/antares-xpansion/benders/factories/BendersFactory.h index 519000516..d4d94ad03 100644 --- a/src/cpp/benders/factories/include/antares-xpansion/benders/factories/BendersFactory.h +++ b/src/cpp/benders/factories/include/antares-xpansion/benders/factories/BendersFactory.h @@ -1,14 +1,11 @@ #ifndef ANTARES_XPANSION_SRC_CPP_BENDERS_FACTORIES_INCLUDE_BENDERSFACTORY_H #define ANTARES_XPANSION_SRC_CPP_BENDERS_FACTORIES_INCLUDE_BENDERSFACTORY_H +#include + #include "antares-xpansion/benders/benders_core/CriterionComputation.h" #include "antares-xpansion/benders/benders_core/common.h" #include "antares-xpansion/benders/benders_mpi/BendersMPI.h" -struct InvalidStructureFile - : public LogUtils::XpansionError { - using LogUtils::XpansionError::XpansionError; -}; - class BendersMainFactory { private: char** argv_; @@ -17,10 +14,13 @@ class BendersMainFactory { SOLVER solver_ = SOLVER::BENDERS; SimulationOptions options_; BendersLoggerBase benders_loggers_; - std::shared_ptr - criterion_computation_ = nullptr; + std::variant + criterion_input_holder_; + pBendersBase benders_ = nullptr; Logger logger_ = nullptr; Writer writer_ = nullptr; + std::shared_ptr math_log_driver_; BENDERSMETHOD method_ = BENDERSMETHOD::BENDERS; std::string context_ = bendersmethod_to_string(BENDERSMETHOD::BENDERS); std::string positive_unsupplied_file_; @@ -30,13 +30,21 @@ class BendersMainFactory { [[nodiscard]] int RunBenders(); [[nodiscard]] std::shared_ptr BuildMathLogger( bool benders_log_console) const; - pBendersBase PrepareForExecution(bool external_loop); - [[nodiscard]] Benders::Criterion::OuterLoopInputData ProcessCriterionInput(); + void PrepareForExecution(bool outer_loop); + [[nodiscard]] std::variant + ProcessCriterionInput(); - Benders::Criterion::OuterLoopInputData BuildPatternsUsingAreaFile(); + Benders::Criterion::CriterionInputData BuildPatternsUsingAreaFile(); std::set ReadAreaFile(); + void StartMessage(); void EndMessage(const double execution_time); - void AddCriterionOutput(std::shared_ptr math_log_driver); + void AddCriterionOutputs(); + bool isCriterionListEmpty() const; + void SetupLoggerAndOutputWriter(const BendersBaseOptions& benders_options); + void ConfigureBenders(const BendersBaseOptions& benders_options, + const CouplingMap& coupling_map); + void ConfigureSolverLog(); public: explicit BendersMainFactory(int argc, char** argv, diff --git a/src/cpp/benders/factories/include/antares-xpansion/benders/factories/LoggerFactories.h b/src/cpp/benders/factories/include/antares-xpansion/benders/factories/LoggerFactories.h index 2b9612e33..5fb258344 100644 --- a/src/cpp/benders/factories/include/antares-xpansion/benders/factories/LoggerFactories.h +++ b/src/cpp/benders/factories/include/antares-xpansion/benders/factories/LoggerFactories.h @@ -12,9 +12,6 @@ #include "antares-xpansion/benders/logger/UserFile.h" Logger build_void_logger(); -std::ostringstream start_message(const SimulationOptions &options, - const std::string &benders_type); - class FileAndStdoutLoggerFactory { private: Logger logger; diff --git a/src/cpp/benders/merge_mps/MergeMPS.cpp b/src/cpp/benders/merge_mps/MergeMPS.cpp index 2b5f910e2..d1679e32b 100644 --- a/src/cpp/benders/merge_mps/MergeMPS.cpp +++ b/src/cpp/benders/merge_mps/MergeMPS.cpp @@ -2,8 +2,7 @@ #include -#include "antares-xpansion/helpers/ArchiveReader.h" -#include "antares-xpansion/xpansion_interfaces/LogUtils.h" +#include "antares-xpansion/benders/benders_core/CouplingMapGenerator.h" #include "antares-xpansion/helpers/Timer.h" MergeMPS::MergeMPS(const MergeMPSOptions &options, Logger &logger, @@ -13,7 +12,8 @@ MergeMPS::MergeMPS(const MergeMPSOptions &options, Logger &logger, void MergeMPS::launch() { const auto inputRootDir = std::filesystem::path(_options.INPUTROOT); auto structure_path(inputRootDir / _options.STRUCTURE_FILE); - CouplingMap input = build_input(structure_path); + CouplingMap input = + CouplingMapGenerator::BuildInput(structure_path, _logger, "Merge mps"); SolverFactory factory; std::string solver_to_use = diff --git a/src/cpp/benders/outer_loop/OuterLoopBiLevel.cpp b/src/cpp/benders/outer_loop/OuterLoopBiLevel.cpp index d687b11e8..8cf3d70aa 100644 --- a/src/cpp/benders/outer_loop/OuterLoopBiLevel.cpp +++ b/src/cpp/benders/outer_loop/OuterLoopBiLevel.cpp @@ -2,7 +2,7 @@ namespace Outerloop { OuterLoopBiLevel::OuterLoopBiLevel( - const std::vector + const std::vector &outer_loop_input_data) : outer_loop_input_data_(outer_loop_input_data) {} diff --git a/src/cpp/benders/outer_loop/include/antares-xpansion/benders/outer_loop/OuterLoopBiLevel.h b/src/cpp/benders/outer_loop/include/antares-xpansion/benders/outer_loop/OuterLoopBiLevel.h index 59971f2b5..76e7c0f97 100644 --- a/src/cpp/benders/outer_loop/include/antares-xpansion/benders/outer_loop/OuterLoopBiLevel.h +++ b/src/cpp/benders/outer_loop/include/antares-xpansion/benders/outer_loop/OuterLoopBiLevel.h @@ -1,5 +1,5 @@ #pragma once -#include "antares-xpansion/benders/benders_core/OuterLoopInputDataReader.h" +#include "antares-xpansion/benders/benders_core/CriterionInputDataReader.h" // TODO typedef std::map Point; @@ -9,7 +9,7 @@ namespace Outerloop { class OuterLoopBiLevel { public: explicit OuterLoopBiLevel( - const std::vector + const std::vector &outer_loop_input_data); bool Update_bilevel_data_if_feasible( const Point &x, const std::vector &outer_loop_criterion, @@ -40,7 +40,7 @@ class OuterLoopBiLevel { double lambda_max_ = 0.0; double lambda_min_ = 0.0; double lambda_ = 0.0; - const std::vector + const std::vector &outer_loop_input_data_; }; } // namespace Outerloop \ No newline at end of file diff --git a/src/cpp/xpansion_interfaces/include/antares-xpansion/xpansion_interfaces/LogUtils.h b/src/cpp/xpansion_interfaces/include/antares-xpansion/xpansion_interfaces/LogUtils.h index b893d0633..f6b8f1d50 100644 --- a/src/cpp/xpansion_interfaces/include/antares-xpansion/xpansion_interfaces/LogUtils.h +++ b/src/cpp/xpansion_interfaces/include/antares-xpansion/xpansion_interfaces/LogUtils.h @@ -4,8 +4,8 @@ namespace LogUtils { inline std::string LogLocationToStr(int line, const char* file, const char* func) { - return std::string("This is line ") + std::to_string(line) + " of file " + - file + " (function " + func + ")\n"; + return std::string("Logged from function '") + func + "' in file '" + file + + "' at line " + std::to_string(line) + ".\n"; } template diff --git a/tests/cpp/outer_loop/outer_loop_test.cpp b/tests/cpp/outer_loop/outer_loop_test.cpp index 725472d49..b203a9350 100644 --- a/tests/cpp/outer_loop/outer_loop_test.cpp +++ b/tests/cpp/outer_loop/outer_loop_test.cpp @@ -1,6 +1,7 @@ +#include "antares-xpansion/benders/benders_core/CouplingMapGenerator.h" +#include "antares-xpansion/benders/benders_core/CriterionInputDataReader.h" #include "antares-xpansion/benders/benders_core/MasterUpdate.h" -#include "antares-xpansion/benders/benders_core/OuterLoopInputDataReader.h" #include "antares-xpansion/benders/benders_core/VariablesGroup.h" #include "antares-xpansion/benders/benders_mpi/OuterLoopBenders.h" #include "antares-xpansion/benders/factories/LoggerFactories.h" @@ -101,9 +102,10 @@ void CheckMinInvestmentConstraint(const VariableMap& master_variables, TEST_P(MasterUpdateBaseTest, ConstraintIsAddedBendersMPI) { BendersBaseOptions bendersoptions = BuildBendersOptions(); - CouplingMap coupling_map = - build_input(std::filesystem::path(bendersoptions.INPUTROOT) / - bendersoptions.STRUCTURE_FILE); + CouplingMap coupling_map = CouplingMapGenerator::BuildInput( + std::filesystem::path(bendersoptions.INPUTROOT) / + bendersoptions.STRUCTURE_FILE, + logger, ::testing::UnitTest::GetInstance()->current_test_info()->name()); // override solver bendersoptions.SOLVER_NAME = GetParam(); bendersoptions.EXTERNAL_LOOP_OPTIONS.DO_OUTER_LOOP = true; @@ -114,19 +116,16 @@ TEST_P(MasterUpdateBaseTest, ConstraintIsAddedBendersMPI) { benders->set_input_map(coupling_map); auto outer_loop_input_data = - Benders::Criterion::OuterLoopInputFromYaml().Read( + Benders::Criterion::CriterionInputFromYaml().Read( std::filesystem::path(bendersoptions.INPUTROOT) / OUTER_OPTIONS_FILE); - auto criterion_computation = - std::make_shared( - outer_loop_input_data); - benders->setCriterionsComputation(criterion_computation); + + benders->setCriterionComputationInputs(outer_loop_input_data); auto master_updater = std::make_shared( benders, 0.5, outer_loop_input_data.StoppingThreshold()); auto cut_manager = std::make_shared(); - Outerloop::OuterLoopBenders out_loop( - criterion_computation->getOuterLoopInputData().OuterLoopData(), - master_updater, cut_manager, benders, *pworld); + Outerloop::OuterLoopBenders out_loop(outer_loop_input_data.Criteria(), + master_updater, cut_manager, benders, *pworld); out_loop.OuterLoopCheckFeasibility(); auto num_constraints_master_before = benders->MasterGetnrows(); @@ -180,7 +179,7 @@ class OuterLoopPatternTest : public ::testing::Test {}; TEST_F(OuterLoopPatternTest, RegexGivenPrefixAndBody) { const std::string prefix = "prefix"; const std::string body = "body"; - OuterLoopPattern o(prefix, body); + CriterionPattern o(prefix, body); auto ret_regex = o.MakeRegex(); @@ -203,9 +202,9 @@ TEST_F(OuterLoopInputFromYamlTest, YamlFileDoesNotExist) { expected_msg << "Could not read outer loop input file: " << empty << "\n" << "bad file: " << empty.string(); try { - OuterLoopInputFromYaml parser; + CriterionInputFromYaml parser; parser.Read(empty); - } catch (const OuterLoopInputFileError& e) { + } catch (const CriterionInputFileError& e) { ASSERT_EQ(expected_msg.str(), e.ErrorMessage()); } } @@ -219,9 +218,9 @@ TEST_F(OuterLoopInputFromYamlTest, YamlFileIsEmpty) { std::ostringstream expected_msg; expected_msg << "outer loop input file is empty: " << empty << "\n"; try { - OuterLoopInputFromYaml parser; + CriterionInputFromYaml parser; parser.Read(empty); - } catch (const OuterLoopInputFileIsEmpty& e) { + } catch (const CriterionInputFileIsEmpty& e) { ASSERT_EQ(expected_msg.str(), e.ErrorMessage()); } } @@ -237,9 +236,9 @@ TEST_F(OuterLoopInputFromYamlTest, YamlFileShouldContainsAtLeast1Pattern) { expected_msg << "outer loop input file must contains at least one pattern." << "\n"; try { - OuterLoopInputFromYaml parser; + CriterionInputFromYaml parser; parser.Read(empty_patterns); - } catch (const OuterLoopInputFileNoPatternFound& e) { + } catch (const CriterionInputFileNoPatternFound& e) { ASSERT_EQ(expected_msg.str(), e.ErrorMessage()); } } @@ -256,9 +255,9 @@ TEST_F(OuterLoopInputFromYamlTest, YamlFilePatternsShouldBeAnArray) { expected_msg << "In outer loop input file 'patterns' should be an array." << "\n"; try { - OuterLoopInputFromYaml parser; + CriterionInputFromYaml parser; parser.Read(patterns_not_array); - } catch (const OuterLoopInputPatternsShouldBeArray& e) { + } catch (const CriterionInputPatternsShouldBeArray& e) { ASSERT_EQ(expected_msg.str(), e.ErrorMessage()); } } @@ -282,12 +281,12 @@ criterion_count_threshold: 1e-1 criterion: 1)"; of << my_yaml; of.close(); - auto data = OuterLoopInputFromYaml().Read(valid_file); + auto data = CriterionInputFromYaml().Read(valid_file); ASSERT_EQ(data.StoppingThreshold(), 1e-4); ASSERT_EQ(data.CriterionCountThreshold(), 1e-1); - auto patterns = data.OuterLoopData(); + auto patterns = data.Criteria(); ASSERT_EQ(patterns.size(), 4); auto pattern1 = patterns[0]; ASSERT_EQ(pattern1.Criterion(), 185.0); @@ -298,7 +297,7 @@ class VariablesGroupTest : public ::testing::Test {}; TEST_F(VariablesGroupTest, EmptyVariablesListGivesEmptyIndices) { std::vector variables; - std::vector data; + std::vector data; Benders::Criterion::VariablesGroup var_grp(variables, data); ASSERT_TRUE(var_grp.Indices().empty()); @@ -307,7 +306,7 @@ TEST_F(VariablesGroupTest, EmptyVariablesListGivesEmptyIndices) { TEST_F(VariablesGroupTest, EmptyPatternsListGivesEmptyIndices) { std::vector variables{ "PositiveUnsuppliedEnergy::area::hour<125>"}; - std::vector data; + std::vector data; Benders::Criterion::VariablesGroup var_grp(variables, data); ASSERT_TRUE(var_grp.Indices().empty()); @@ -316,8 +315,8 @@ TEST_F(VariablesGroupTest, EmptyPatternsListGivesEmptyIndices) { TEST_F(VariablesGroupTest, SingleDataWithInvalidPrefixAndBody) { std::vector variables{ "PositiveUnsuppliedEnergy::area::hour<125>"}; - std::vector data{ - Benders::Criterion::OuterLoopSingleInputData("Pref", "Body", 1534.0)}; + std::vector data{ + Benders::Criterion::CriterionSingleInputData("Pref", "Body", 1534.0)}; Benders::Criterion::VariablesGroup var_grp(variables, data); const auto& vect_indices = var_grp.Indices(); @@ -328,8 +327,8 @@ TEST_F(VariablesGroupTest, SingleDataWithInvalidPrefixAndBody) { TEST_F(VariablesGroupTest, SingleDataWithUnMatchedPrefix) { std::vector variables{ "PositiveUnsuppliedEnergy::area::hour<125>"}; - std::vector data{ - Benders::Criterion::OuterLoopSingleInputData("UnsuppliedEnergy::", "test", + std::vector data{ + Benders::Criterion::CriterionSingleInputData("UnsuppliedEnergy::", "test", 1534.0)}; Benders::Criterion::VariablesGroup var_grp(variables, data); @@ -341,8 +340,8 @@ TEST_F(VariablesGroupTest, SingleDataWithUnMatchedPrefix) { TEST_F(VariablesGroupTest, SingleDataWithUnMatchedBody) { std::vector variables{ "PositiveUnsuppliedEnergy::area::hour<125>"}; - std::vector data{ - Benders::Criterion::OuterLoopSingleInputData( + std::vector data{ + Benders::Criterion::CriterionSingleInputData( "PositiveUnsuppliedEnergy::", "Body", 1534.0)}; Benders::Criterion::VariablesGroup var_grp(variables, data); @@ -351,7 +350,7 @@ TEST_F(VariablesGroupTest, SingleDataWithUnMatchedBody) { ASSERT_TRUE(vect_indices[0].empty()); } -static const std::vector data{ +static const std::vector data{ {"Blue::", "Earth", 1534.0}, {"Red::", "Mars", 65.0}}; TEST_F(VariablesGroupTest, With2ValidPatterns) { @@ -381,24 +380,28 @@ TEST_F(OuterLoopBiLevelTest, BiLevelBestUbInitialization) { ASSERT_EQ(outerLoopBiLevel.BilevelBestub(), 1e20); } -TEST_F(OuterLoopBiLevelTest, Unfeasibility_With_High_Criterions) { +TEST_F(OuterLoopBiLevelTest, Unfeasibility_With_High_Criteria) { Outerloop::OuterLoopBiLevel outerLoopBiLevel(data); - const std::vector criterions = {data[0].Criterion() + 12, - data[1].Criterion() + 2024}; + const std::vector criteria = { + data[0].Criterion() + 12, + data[1].Criterion() + 2024 + }; const auto lambda = 1205; ASSERT_FALSE(outerLoopBiLevel.Update_bilevel_data_if_feasible( - {}, criterions, 0., 0., lambda)); + {}, criteria, 0., 0., lambda)); ASSERT_EQ(outerLoopBiLevel.LambdaMin(), lambda); } TEST_F(OuterLoopBiLevelTest, UnfeasibilityWithInfiniteOverAllCosts) { Outerloop::OuterLoopBiLevel outerLoopBiLevel(data); - const std::vector criterions = {data[0].Criterion() - 12, - data[1].Criterion() - 24}; + const std::vector criteria = { + data[0].Criterion() - 12, + data[1].Criterion() - 24 + }; const auto lambda = 1205; const auto overall_cost = 1e20; ASSERT_FALSE(outerLoopBiLevel.Update_bilevel_data_if_feasible( - {}, criterions, overall_cost, 0., lambda)); + {}, criteria, overall_cost, 0., lambda)); ASSERT_EQ(outerLoopBiLevel.LambdaMin(), lambda); } @@ -412,12 +415,14 @@ TEST_F(OuterLoopBiLevelTest, FeasibleScenario) { const auto cand_coeff = 2.0; outerLoopBiLevel.Init({cand_coeff}, max_x, {{cand, 0}}); ASSERT_EQ(outerLoopBiLevel.LambdaMax(), cand_coeff * max_x.at(cand)); - const std::vector criterions = {data[0].Criterion() - 12, - data[1].Criterion() - 24}; + const std::vector criteria = { + data[0].Criterion() - 12, + data[1].Criterion() - 24 + }; const Point x = {{cand, max_x.at(cand) / 2}}; ASSERT_TRUE(outerLoopBiLevel.Update_bilevel_data_if_feasible( - x, criterions, overall_cost, invest_cost_at_x, lambda)); + x, criteria, overall_cost, invest_cost_at_x, lambda)); ASSERT_EQ(outerLoopBiLevel.LambdaMin(), 0.); ASSERT_EQ(outerLoopBiLevel.LambdaMax(), invest_cost_at_x); ASSERT_EQ(outerLoopBiLevel.BilevelBestub(), overall_cost); @@ -437,12 +442,14 @@ TEST_F(OuterLoopBiLevelTest, // higher than lambda_max initial value const auto invest_cost_at_x = initial_lambda_max * 30; ASSERT_EQ(outerLoopBiLevel.LambdaMax(), initial_lambda_max); - const std::vector criterions = {data[0].Criterion() - 12, - data[1].Criterion() - 24}; + const std::vector criteria = { + data[0].Criterion() - 12, + data[1].Criterion() - 24 + }; const Point x = {{cand, max_x.at(cand) / 2}}; ASSERT_TRUE(outerLoopBiLevel.Update_bilevel_data_if_feasible( - x, criterions, overall_cost, invest_cost_at_x, lambda)); + x, criteria, overall_cost, invest_cost_at_x, lambda)); ASSERT_EQ(outerLoopBiLevel.LambdaMin(), 0.); ASSERT_EQ(outerLoopBiLevel.LambdaMax(), initial_lambda_max); ASSERT_EQ(outerLoopBiLevel.BilevelBestub(), overall_cost);