diff --git a/cmake/dependencies/CMakeLists.txt b/cmake/dependencies/CMakeLists.txt index e65c3c3b1..b8ee01495 100644 --- a/cmake/dependencies/CMakeLists.txt +++ b/cmake/dependencies/CMakeLists.txt @@ -9,7 +9,7 @@ if (NOT antares-solver_FOUND) set(REPOSITORY "https://github.com/AntaresSimulatorTeam/Antares_Simulator.git") set(TAG "v${ANTARES_VERSION_TAG}") - set(CMAKE_ARGS "-DBUILD_UI=OFF -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DDEPS_INSTALL_DIR=${DEPS_INSTALL_DIR} -DBUILD_not_system=OFF -DBUILD_ortools=ON") + set(CMAKE_ARGS "-DBUILD_UI=OFF -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DDEPS_INSTALL_DIR=${DEPS_INSTALL_DIR} -DBUILD_not_system=OFF -DBUILD_ortools=ON -DCMAKE_PREFIX_PATH=${DEPS_INSTALL_DIR}/../antares-xpansion/vcpkg_installed") if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(ANTARES_BUILD_TYPE "debug") diff --git a/docs/user-guide/get-started/settings-definition.md b/docs/user-guide/get-started/settings-definition.md index 1f71e190c..f7448926c 100644 --- a/docs/user-guide/get-started/settings-definition.md +++ b/docs/user-guide/get-started/settings-definition.md @@ -21,6 +21,7 @@ The following section lists the configurable parameters. If the user does not sp |[`separation_parameter`](#separation_parameter) | `0.5` | Step size for the in-out separation | |[`relaxed_optimality_gap`](#relaxed_optimality_gap) | `1e-5` | Threshold to switch from relaxed to integer master | |[`batch_size`](#batch_size) | `0` | Number of subproblems per batch | +|[`expert_logs`](#expert_logs) | `false` | display pure optimization data (/!\ the corresponding option(`EXPERT_LOGS`) for benders is `true` by default ) | The format is a standard `.ini` and should follow this template: ```ini diff --git a/src/cpp/benders/benders_by_batch/BendersByBatch.cpp b/src/cpp/benders/benders_by_batch/BendersByBatch.cpp index fb2a72826..0e8aba252 100644 --- a/src/cpp/benders/benders_by_batch/BendersByBatch.cpp +++ b/src/cpp/benders/benders_by_batch/BendersByBatch.cpp @@ -8,10 +8,11 @@ #include "BatchCollection.h" #include "RandomBatchShuffler.h" #include "glog/logging.h" -BendersByBatch::BendersByBatch(BendersBaseOptions const &options, Logger logger, - Writer writer, mpi::environment &env, - mpi::communicator &world) - : BendersMpi(options, logger, writer, env, world) {} +BendersByBatch::BendersByBatch( + BendersBaseOptions const &options, Logger logger, Writer writer, + mpi::environment &env, mpi::communicator &world, + std::shared_ptr mathLoggerDriver) + : BendersMpi(options, logger, writer, env, world, mathLoggerDriver) {} void BendersByBatch::InitializeProblems() { MatchProblemToId(); @@ -75,8 +76,8 @@ void BendersByBatch::MasterLoop() { random_batch_permutation_.resize(number_of_batch_); batch_counter_ = 0; current_batch_id_ = 0; - - _data.number_of_subproblem_resolved = 0; + _data.number_of_subproblem_solved = 0; + _data.cumulative_number_of_subproblem_solved = 0; cumulative_subproblems_timer_per_iter_ = 0; first_unsolved_batch_ = 0; while (!_data.stop) { @@ -112,21 +113,22 @@ void BendersByBatch::MasterLoop() { random_batch_permutation_.size(), rank_0); SeparationLoop(); if (Rank() == rank_0) { - _data.elapsed_time = GetBendersTime(); _data.stop = ShouldBendersStop(); } BroadCast(_data.stop, rank_0); BroadCast(batch_counter_, rank_0); SetSubproblemsCumulativeCpuTime(cumulative_subproblems_timer_per_iter_); - _logger->cumulative_number_of_sub_problem_resolved( - _data.number_of_subproblem_resolved + - GetNumOfSubProblemsResolvedBeforeResume()); + _logger->cumulative_number_of_sub_problem_solved( + _data.cumulative_number_of_subproblem_solved + + GetNumOfSubProblemsSolvedBeforeResume()); _logger->LogSubproblemsSolvingCumulativeCpuTime( GetSubproblemsCumulativeCpuTime()); _logger->LogSubproblemsSolvingWalltime(GetSubproblemsWalltime()); _logger->display_message( "\\________________________________________________________________" "________"); + _data.elapsed_time = GetBendersTime(); + mathLoggerDriver_->Print(_data); } } void BendersByBatch::SeparationLoop() { @@ -135,13 +137,16 @@ void BendersByBatch::SeparationLoop() { batch_counter_ = 0; while (misprice_ && batch_counter_ < number_of_batch_) { _data.it++; + ResetSimplexIterationsBounds(); _logger->log_at_initialization(_data.it + GetNumIterationsBeforeRestart()); ComputeXCut(); _logger->log_iteration_candidates(bendersDataToLogData(_data)); BroadcastXCut(); UpdateRemainingEpsilon(); + _data.number_of_subproblem_solved = 0; SolveBatches(); + if (Rank() == rank_0) { UpdateTrace(); SaveCurrentBendersData(); @@ -195,7 +200,8 @@ void BendersByBatch::SolveBatches() { Reduce(GetSubproblemsCpuTime(), cumulative_subproblems_timer_per_iter_, std::plus(), rank_0); if (Rank() == rank_0) { - _data.number_of_subproblem_resolved += batch_sub_problems.size(); + _data.number_of_subproblem_solved += batch_sub_problems.size(); + _data.cumulative_number_of_subproblem_solved += batch_sub_problems.size(); remaining_epsilon_ -= batch_subproblems_costs_contribution_in_gap; } @@ -229,6 +235,7 @@ void BendersByBatch::BuildCut( for (const auto &subproblem_map : gathered_subproblem_map) { for (auto &&[_, subproblem_data] : subproblem_map) { SetSubproblemCost(GetSubproblemCost() + subproblem_data.subproblem_cost); + BoundSimplexIterations(subproblem_data.simplex_iter); } } for (const auto &subproblem_map : gathered_subproblem_map) { diff --git a/src/cpp/benders/benders_by_batch/include/BendersByBatch.h b/src/cpp/benders/benders_by_batch/include/BendersByBatch.h index 9590f2726..06af08e98 100644 --- a/src/cpp/benders/benders_by_batch/include/BendersByBatch.h +++ b/src/cpp/benders/benders_by_batch/include/BendersByBatch.h @@ -9,8 +9,8 @@ class BendersByBatch : public BendersMpi { public: BendersByBatch(BendersBaseOptions const &options, Logger logger, - Writer writer, mpi::environment &env, - mpi::communicator &world); + Writer writer, mpi::environment &env, mpi::communicator &world, + std::shared_ptr mathLoggerDriver); ~BendersByBatch() override = default; void Run() override; void BuildCut(const std::vector &batch_sub_problems, diff --git a/src/cpp/benders/benders_core/BendersBase.cpp b/src/cpp/benders/benders_core/BendersBase.cpp index 71e239603..ad461521b 100644 --- a/src/cpp/benders/benders_core/BendersBase.cpp +++ b/src/cpp/benders/benders_core/BendersBase.cpp @@ -13,12 +13,14 @@ #include "solver_utils.h" BendersBase::BendersBase(BendersBaseOptions options, Logger logger, - Writer writer) + Writer writer, + std::shared_ptr mathLoggerDriver) : _options(std::move(options)), _csv_file_path(std::filesystem::path(_options.OUTPUTROOT) / (_options.CSV_NAME + ".csv")), _logger(std::move(logger)), - _writer(std::move(writer)) {} + _writer(std::move(writer)), + mathLoggerDriver_(mathLoggerDriver) {} /*! * \brief Initialize set of data used in the loop @@ -34,7 +36,7 @@ void BendersBase::init_data() { _data.best_it = 0; _data.stopping_criterion = StoppingCriterion::empty; _data.is_in_initial_relaxation = false; - _data.number_of_subproblem_resolved = 0; + _data.cumulative_number_of_subproblem_solved = 0; } void BendersBase::OpenCsvFile() { @@ -467,7 +469,7 @@ LogData BendersBase::FinalLogData() const { result.subproblem_cost = best_iteration_data.subproblem_cost; result.invest_cost = best_iteration_data.invest_cost; result.cumulative_number_of_subproblem_resolved = - _data.number_of_subproblem_resolved + + _data.cumulative_number_of_subproblem_solved + cumulative_number_of_subproblem_resolved_before_resume; return result; @@ -525,7 +527,7 @@ Output::Iteration BendersBase::iteration( masterDataPtr_l->_invest_cost + masterDataPtr_l->_operational_cost; iteration.candidates = candidates_data(masterDataPtr_l); iteration.cumulative_number_of_subproblem_resolved = - _data.number_of_subproblem_resolved + + _data.cumulative_number_of_subproblem_solved + cumulative_number_of_subproblem_resolved_before_resume; return iteration; } @@ -651,7 +653,7 @@ LogData BendersBase::bendersDataToLogData( data.elapsed_time, data.timer_master, data.subproblems_walltime, - data.number_of_subproblem_resolved + + data.cumulative_number_of_subproblem_solved + cumulative_number_of_subproblem_resolved_before_resume}; } void BendersBase::set_log_file(const std::filesystem::path &log_file) { @@ -773,6 +775,24 @@ void BendersBase::SetSubproblemCost(const double &subproblem_cost) { _data.subproblem_cost = subproblem_cost; } +/*! +* \brief Update maximum and minimum of simplex iterations +* +* \param subproblem_iterations : number of iterations done with the subproblem +* +*/ +void BendersBase::BoundSimplexIterations(int subproblem_iterations){ + + _data.max_simplexiter = (_data.max_simplexiter < subproblem_iterations) ? subproblem_iterations : _data.max_simplexiter; + _data.min_simplexiter = (_data.min_simplexiter > subproblem_iterations) ? subproblem_iterations : _data.min_simplexiter; + +} + +void BendersBase::ResetSimplexIterationsBounds() +{ + _data.max_simplexiter = 0; + _data.min_simplexiter = std::numeric_limits::max(); +} bool BendersBase::IsResumeMode() const { return _options.RESUME; } void BendersBase::UpdateMaxNumberIterationResumeMode( @@ -793,7 +813,7 @@ LogData BendersBase::GetBestIterationData() const { } void BendersBase::ChecksResumeMode() { - benders_timer = Timer(); + ITE_TIMEr = Timer(); if (IsResumeMode()) { auto reader = LastIterationReader(LastIterationFile()); LogData last_iter; @@ -809,7 +829,7 @@ void BendersBase::ChecksResumeMode() { LastIterationPrinter(_logger, best_iteration_data, last_iter); restart_data_printer.Print(); UpdateMaxNumberIterationResumeMode(last_iter.it); - benders_timer = Timer(last_iter.benders_elapsed_time); + ITE_TIMEr = Timer(last_iter.benders_elapsed_time); _data.stop = ShouldBendersStop(); iterations_before_resume = last_iter.it; cumulative_number_of_subproblem_resolved_before_resume = @@ -836,7 +856,7 @@ void BendersBase::EndWritingInOutputFile() const { _writer->write_duration(_data.elapsed_time); SaveSolutionInOutputFile(); } -double BendersBase::GetBendersTime() const { return benders_timer.elapsed(); } +double BendersBase::GetBendersTime() const { return ITE_TIMEr.elapsed(); } void BendersBase::write_basis() const { const auto filename(std::filesystem::path(_options.OUTPUTROOT) / (_options.LAST_MASTER_BASIS)); diff --git a/src/cpp/benders/benders_core/BendersMathLogger.cpp b/src/cpp/benders/benders_core/BendersMathLogger.cpp new file mode 100644 index 000000000..bca9d460e --- /dev/null +++ b/src/cpp/benders/benders_core/BendersMathLogger.cpp @@ -0,0 +1,82 @@ +#include "BendersMathLogger.h" + +// #ifdef _WIN32 +// #include +// #endif + +HeadersManager::HeadersManager(HEADERSTYPE type, const BENDERSMETHOD& method) { + if (type == HEADERSTYPE::SHORT) { + headers_list.push_back("ITE"); + headers_list.push_back("LB"); + if (method == BENDERSMETHOD::BENDERS) { + headers_list.push_back("UB"); + headers_list.push_back("BESTUB"); + headers_list.push_back("AGAP"); + headers_list.push_back("RGAP"); + } + headers_list.push_back("MinSpx"); + headers_list.push_back("MaxSpx"); + if (method == BENDERSMETHOD::BENDERSBYBATCH) { + headers_list.push_back("NbSubPbSolv"); + } + headers_list.push_back("IteTime"); + headers_list.push_back("MasterTime"); + // headers_list.push_back("SubPbCpuTime"); + headers_list.push_back("SPWallTime"); + } else { + headers_list.push_back("ITERATION"); + headers_list.push_back("LB"); + if (method == BENDERSMETHOD::BENDERS) { + headers_list.push_back("UB"); + headers_list.push_back("BESTUB"); + headers_list.push_back("ABSOLUTE GAP"); + headers_list.push_back("RELATIVE GAP"); + } + headers_list.push_back("MINSIMPLEX"); + headers_list.push_back("MAXSIMPLEX"); + headers_list.push_back("NUMBER OF SUBPROBLEMS SOLVED"); + headers_list.push_back("CUMULATIVE NUMBER OF SUBPROBLEMS SOLVED "); + headers_list.push_back("ITERATION TIME"); + headers_list.push_back("MASTER TIME"); + headers_list.push_back("SUB-PROBLEMS TIME (CPU)"); + headers_list.push_back("SUB-PROBLEMS TIME (WALL)"); + headers_list.push_back("TIME NOT DOING MASTER OR SUB-PROBLEMS (WALL)"); + } +} + +LogDestination::LogDestination(std::ostream* stream, std::streamsize width) + : stream_(stream), width_(width) { + // _COORD coordinates; + // coordinates.X = 1000; + // coordinates.Y = 1000; + + // if (0 == SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), + // coordinates)) { + // std::cout << "could not resize the console screen\n"; + // // return -1; + // } +} +void MathLoggerDriver::add_logger( + std::shared_ptr logger) { + if (logger) { + math_loggers_.push_back(logger); + } +} + +void MathLoggerDriver::Print(const CurrentIterationData& data) { + for (auto logger : math_loggers_) { + logger->Print(data); + } +} + +void MathLoggerDriver::write_header() { + for (auto logger : math_loggers_) { + logger->write_header(); + } +} + +void MathLoggerDriver::display_message(const std::string& str) { + for (auto logger : math_loggers_) { + logger->display_message(str); + } +} diff --git a/src/cpp/benders/benders_core/BendersStructsDatas.cpp b/src/cpp/benders/benders_core/BendersStructsDatas.cpp index bea2aff25..9b5e8dc8a 100644 --- a/src/cpp/benders/benders_core/BendersStructsDatas.cpp +++ b/src/cpp/benders/benders_core/BendersStructsDatas.cpp @@ -8,3 +8,4 @@ Point WorkerMasterData::get_x_cut() const { return *_x_cut; } Point WorkerMasterData::get_min_invest() const { return *_min_invest; } Point WorkerMasterData::get_max_invest() const { return *_max_invest; } + diff --git a/src/cpp/benders/benders_core/CMakeLists.txt b/src/cpp/benders/benders_core/CMakeLists.txt index 05f6af399..c889a00db 100644 --- a/src/cpp/benders/benders_core/CMakeLists.txt +++ b/src/cpp/benders/benders_core/CMakeLists.txt @@ -26,7 +26,9 @@ add_library (benders_core STATIC ${CMAKE_CURRENT_SOURCE_DIR}/LastIterationWriter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/LastIterationReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/LastIterationPrinter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/StartUp.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/StartUp.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BendersMathLogger.cpp + ) get_target_property(JSON_INC_PATH jsoncpp_lib INTERFACE_INCLUDE_DIRECTORIES) diff --git a/src/cpp/benders/benders_core/SimulationOptions.cpp b/src/cpp/benders/benders_core/SimulationOptions.cpp index 22b64c9cc..d59440231 100644 --- a/src/cpp/benders/benders_core/SimulationOptions.cpp +++ b/src/cpp/benders/benders_core/SimulationOptions.cpp @@ -112,7 +112,7 @@ void SimulationOptions::set_weights() { void SimulationOptions::print(std::ostream &stream) const { #define BENDERS_OPTIONS_MACRO(name__, type__, default__, \ deserialization_method__) \ - stream << std::setw(30) << #name__ << std::setw(50) << name__ << std::endl; + stream << std::setw(30) << #name__ << std::setw(50)< #include +#include "BendersMathLogger.h" #include "BendersStructsDatas.h" #include "ILogger.h" #include "OutputWriter.h" @@ -29,7 +30,8 @@ auto selectPolicy(lambda f, bool shouldParallelize) { class BendersBase { public: virtual ~BendersBase() = default; - BendersBase(BendersBaseOptions options, Logger logger, Writer writer); + BendersBase(BendersBaseOptions options, Logger logger, Writer writer, + std::shared_ptr mathLoggerDriver); virtual void launch() = 0; void set_log_file(const std::filesystem::path &log_name); [[nodiscard]] std::filesystem::path log_name() const { return _log_name; } @@ -40,6 +42,7 @@ class BendersBase { CurrentIterationData _data; VariableMap master_variable_map; CouplingMap coupling_map; + std::shared_ptr mathLoggerDriver_; protected: virtual void free() = 0; @@ -132,10 +135,13 @@ class BendersBase { BendersBaseOptions Options() const { return _options; } virtual void UpdateStoppingCriterion(); virtual bool ShouldRelaxationStop() const; - int GetNumOfSubProblemsResolvedBeforeResume() { + int GetNumOfSubProblemsSolvedBeforeResume() { return cumulative_number_of_subproblem_resolved_before_resume; } + void BoundSimplexIterations(int subproblem_iteration); + void ResetSimplexIterationsBounds(); + SolverLogManager solver_log_manager_; private: @@ -171,7 +177,7 @@ class BendersBase { LogData best_iteration_data; int iterations_before_resume = 0; int cumulative_number_of_subproblem_resolved_before_resume = 0; - Timer benders_timer; + Timer ITE_TIMEr; public: Logger _logger; diff --git a/src/cpp/benders/benders_core/include/BendersMathLogger.h b/src/cpp/benders/benders_core/include/BendersMathLogger.h new file mode 100644 index 000000000..4844b7580 --- /dev/null +++ b/src/cpp/benders/benders_core/include/BendersMathLogger.h @@ -0,0 +1,152 @@ +#pragma once + +#include +#include +#include +#include + +#include "BendersStructsDatas.h" +#include "ILogger.h" +#include "common.h" +const std::string MATHLOGGERCONTEXT = "Benders"; + +struct Header { + std::string label; + int pos; +}; + +enum class HEADERSTYPE { SHORT, LONG }; +struct HeadersManager { + explicit HeadersManager(HEADERSTYPE type, const BENDERSMETHOD& method); + std::vector headers_list; +}; +class LogDestination { + public: + explicit LogDestination(std::ostream* stream, std::streamsize width = 40); + + // for std::endl + std::ostream& operator<<(std::ostream& (*function)(std::ostream&)) { + // write obj to stream + return function(*stream_); + } + + template + std::ostream& operator<<(const T& obj); + + private: + std::ostream* stream_; + std::streamsize width_ = 40; +}; +template +std::ostream& LogDestination::operator<<(const T& obj) { + // write obj to stream + return (*stream_) << std::left << std::setw(width_) << obj; +} + +struct MathLoggerBehaviour : public ILoggerBenders { + void write_header() { + setHeadersList(); + for (const auto& header : Headers()) { + LogsDestination() << header; + } + LogsDestination() << std::endl; + } + + virtual void display_message(const std::string& str) { + LogsDestination() << str; + } + + virtual void Print(const CurrentIterationData& data) = 0; + virtual std::vector Headers() const = 0; + virtual LogDestination& LogsDestination() = 0; + virtual void setHeadersList() = 0; +}; + +struct MathLogger : public MathLoggerBehaviour { + explicit MathLogger(std::ostream* stream, std::streamsize width = 40, + HEADERSTYPE type = HEADERSTYPE::LONG) + : log_destination_(stream, width), type_(type) {} + + explicit MathLogger(std::streamsize width = 40, + HEADERSTYPE type = HEADERSTYPE::LONG) + : log_destination_(&std::cout, width), type_(type) {} + + virtual void Print(const CurrentIterationData& data) = 0; + std::vector Headers() const override { return headers_; } + virtual LogDestination& LogsDestination() { return log_destination_; } + virtual void setHeadersList() = 0; + HEADERSTYPE HeadersType() const { return type_; } + + protected: + void setHeadersList(const std::vector& headers); + + private: + std::vector headers_; + LogDestination log_destination_; + HEADERSTYPE type_; +}; + +struct MathLoggerBase : public MathLogger { + using MathLogger::MathLogger; + void Print(const CurrentIterationData& data) override; + + void setHeadersList() override; +}; + +struct MathLoggerBendersByBatch : public MathLogger { + using MathLogger::MathLogger; + void Print(const CurrentIterationData& data) override; + void setHeadersList() override; +}; + +class MathLoggerImplementation : public MathLoggerBehaviour { + public: + explicit MathLoggerImplementation(const BENDERSMETHOD& method, + std::ostream* stream, + std::streamsize width = 40, + HEADERSTYPE type = HEADERSTYPE::LONG) { + if (method == BENDERSMETHOD::BENDERS) { + implementation_ = std::make_shared(stream, width, type); + } else if (method == BENDERSMETHOD::BENDERSBYBATCH) { + implementation_ = + std::make_shared(stream, width, type); + } + // else + } + explicit MathLoggerImplementation(const BENDERSMETHOD& method, + std::streamsize width = 40, + HEADERSTYPE type = HEADERSTYPE::LONG) { + if (method == BENDERSMETHOD::BENDERS) { + implementation_ = std::make_shared(width, type); + } else if (method == BENDERSMETHOD::BENDERSBYBATCH) { + implementation_ = std::make_shared(width, type); + } + // else } + } + + void Print(const CurrentIterationData& data) { implementation_->Print(data); } + + protected: + void setHeadersList() override { implementation_->setHeadersList(); } + std::vector Headers() const override { + return implementation_->Headers(); + } + virtual LogDestination& LogsDestination() { + return implementation_->LogsDestination(); + } + + private: + std::shared_ptr implementation_; +}; + +class MathLoggerDriver : public ILoggerBenders { + public: + MathLoggerDriver() = default; + void write_header(); + void display_message(const std::string& str) override; + void add_logger(std::shared_ptr logger); + void Print(const CurrentIterationData& data); + + private: + std::vector> math_loggers_; +}; diff --git a/src/cpp/benders/benders_core/include/BendersStructsDatas.h b/src/cpp/benders/benders_core/include/BendersStructsDatas.h index c3a8e4468..22d0f6de9 100644 --- a/src/cpp/benders/benders_core/include/BendersStructsDatas.h +++ b/src/cpp/benders/benders_core/include/BendersStructsDatas.h @@ -1,9 +1,9 @@ #pragma once +#include "ILogger.h" #include "SubproblemCut.h" #include "Worker.h" #include "common.h" -#include "ILogger.h" struct CurrentIterationData { double subproblems_walltime; @@ -31,7 +31,10 @@ struct CurrentIterationData { double elapsed_time; StoppingCriterion stopping_criterion; bool is_in_initial_relaxation; - int number_of_subproblem_resolved; + int number_of_subproblem_solved; + int cumulative_number_of_subproblem_solved; + int min_simplexiter; + int max_simplexiter; }; /*! * \class WorkerMasterData diff --git a/src/cpp/benders/benders_core/include/SimulationOptions.hxx b/src/cpp/benders/benders_core/include/SimulationOptions.hxx index 3b216a185..e49a90009 100644 --- a/src/cpp/benders/benders_core/include/SimulationOptions.hxx +++ b/src/cpp/benders/benders_core/include/SimulationOptions.hxx @@ -56,7 +56,7 @@ BENDERS_OPTIONS_MACRO(SOLVER_NAME, std::string, "COIN", asString()) // json file in output/expansion/ BENDERS_OPTIONS_MACRO(JSON_FILE, std::string, ".", asString()) -// last iteration json file in output/expansion/ +// last iteration json file in output/expansion/ BENDERS_OPTIONS_MACRO(LAST_ITERATION_JSON_FILE, std::string, ".", asString()) // TIME_LIMIT BENDERS_OPTIONS_MACRO(TIME_LIMIT, double, 1e12, asDouble()) @@ -72,3 +72,6 @@ BENDERS_OPTIONS_MACRO(LAST_MASTER_BASIS, std::string, "master_last_basis", // BATCH SIZE (Benders by batch) BENDERS_OPTIONS_MACRO(BATCH_SIZE, size_t, 0, asUInt()) + +// which logs must be printed in the console +BENDERS_OPTIONS_MACRO(EXPERT_LOGS, bool, true, asBool()) diff --git a/src/cpp/benders/benders_core/include/common.h b/src/cpp/benders/benders_core/include/common.h index b12ab95d3..51f956eb7 100644 --- a/src/cpp/benders/benders_core/include/common.h +++ b/src/cpp/benders/benders_core/include/common.h @@ -49,6 +49,8 @@ typedef std::vector ActiveCutStorage; typedef std::pair mps_coupling; typedef std::list mps_coupling_list; +enum class BENDERSMETHOD { BENDERS, BENDERSBYBATCH, MERGEMPS }; + struct Predicate { bool operator()(PointPtr const &lhs, PointPtr const &rhs) const { return *lhs < *rhs; @@ -157,6 +159,7 @@ struct BendersBaseOptions : public BaseOptions { std::string LAST_MASTER_BASIS; size_t BATCH_SIZE; + bool EXPERT_LOGS = true; }; void usage(int argc); diff --git a/src/cpp/benders/benders_mpi/BendersMPI.cpp b/src/cpp/benders/benders_mpi/BendersMPI.cpp index 5c86a6f35..49ba18e9c 100644 --- a/src/cpp/benders/benders_mpi/BendersMPI.cpp +++ b/src/cpp/benders/benders_mpi/BendersMPI.cpp @@ -9,8 +9,9 @@ BendersMpi::BendersMpi(BendersBaseOptions const &options, Logger logger, Writer writer, mpi::environment &env, - mpi::communicator &world) - : BendersBase(options, logger, std::move(writer)), + mpi::communicator &world, + std::shared_ptr mathLoggerDriver) + : BendersBase(options, logger, std::move(writer), mathLoggerDriver), _env(env), _world(world) {} @@ -120,10 +121,10 @@ void BendersMpi::step_2_solve_subproblems_and_build_cuts() { check_if_some_proc_had_a_failure(success); gather_subproblems_cut_package_and_build_cuts(subproblem_data_map, walltime); if (Rank() == rank_0) { - _data.number_of_subproblem_resolved += _data.nsubproblem; - _logger->cumulative_number_of_sub_problem_resolved( - _data.number_of_subproblem_resolved + - GetNumOfSubProblemsResolvedBeforeResume()); + _data.cumulative_number_of_subproblem_solved += _data.nsubproblem; + _logger->cumulative_number_of_sub_problem_solved( + _data.cumulative_number_of_subproblem_solved + + GetNumOfSubProblemsSolvedBeforeResume()); } } @@ -137,6 +138,7 @@ void BendersMpi::gather_subproblems_cut_package_and_build_cuts( Reduce(GetSubproblemsCpuTime(), cumulative_subproblems_timer_per_iter, std::plus(), rank_0); SetSubproblemsCumulativeCpuTime(cumulative_subproblems_timer_per_iter); + // only rank_0 receive non-emtpy gathered_subproblem_map master_build_cuts(gathered_subproblem_map); } } @@ -153,6 +155,8 @@ void BendersMpi::master_build_cuts( for (const auto &subproblem_data_map : gathered_subproblem_map) { for (auto &&[_, subproblem_data] : subproblem_data_map) { SetSubproblemCost(GetSubproblemCost() + subproblem_data.subproblem_cost); + // compute delta_cut >= options.CUT_MASTER_TOL; + BoundSimplexIterations(subproblem_data.simplex_iter); } } @@ -189,8 +193,7 @@ void BendersMpi::write_exception_message(const std::exception &ex) const { _logger->display_message(error); } -void BendersMpi::step_4_update_best_solution(int rank, - const Timer &timer_master) { +void BendersMpi::step_4_update_best_solution(int rank) { if (rank == rank_0) { compute_ub(); update_best_ub(); @@ -198,8 +201,6 @@ void BendersMpi::step_4_update_best_solution(int rank, UpdateTrace(); - _data.elapsed_time = GetBendersTime(); - set_timer_master(timer_master.elapsed()); _data.stop = ShouldBendersStop(); } } @@ -224,9 +225,10 @@ void BendersMpi::free() { */ void BendersMpi::Run() { PreRunInitialization(); + _data.number_of_subproblem_solved = _data.nsubproblem; while (!_data.stop) { - Timer timer_master; ++_data.it; + ResetSimplexIterationsBounds(); /*Solve Master problem, get optimal value and cost and send it to process*/ step_1_solve_master(); @@ -238,13 +240,16 @@ void BendersMpi::Run() { } if (!_exceptionRaised) { - step_4_update_best_solution(_world.rank(), timer_master); + step_4_update_best_solution(_world.rank()); } _data.stop |= _exceptionRaised; broadcast(_world, _data.is_in_initial_relaxation, rank_0); broadcast(_world, _data.stop, rank_0); - if (_world.rank() == rank_0) { + + _data.elapsed_time = GetBendersTime(); + mathLoggerDriver_->Print(_data); + if (Rank() == rank_0) { SaveCurrentBendersData(); } } @@ -274,7 +279,9 @@ void BendersMpi::PreRunInitialization() { OpenCsvFile(); } } + mathLoggerDriver_->write_header(); } + void BendersMpi::launch() { build_input_map(); _world.barrier(); diff --git a/src/cpp/benders/benders_mpi/include/BendersMPI.h b/src/cpp/benders/benders_mpi/include/BendersMPI.h index 702bc6d4a..fd3bedaa5 100644 --- a/src/cpp/benders/benders_mpi/include/BendersMPI.h +++ b/src/cpp/benders/benders_mpi/include/BendersMPI.h @@ -19,7 +19,8 @@ class BendersMpi : public BendersBase { public: ~BendersMpi() override = default; BendersMpi(BendersBaseOptions const &options, Logger logger, Writer writer, - mpi::environment &env, mpi::communicator &world); + mpi::environment &env, mpi::communicator &world, + std::shared_ptr mathLoggerDriver); void launch() override; virtual std::string BendersName() const { return "Benders mpi"; } @@ -34,7 +35,7 @@ class BendersMpi : public BendersBase { private: void step_1_solve_master(); void step_2_solve_subproblems_and_build_cuts(); - void step_4_update_best_solution(int rank, const Timer &timer_master); + void step_4_update_best_solution(int rank); void master_build_cuts( std::vector gathered_subproblem_map); diff --git a/src/cpp/benders/benders_sequential/BendersSequential.cpp b/src/cpp/benders/benders_sequential/BendersSequential.cpp index e595ba17d..e1be67864 100644 --- a/src/cpp/benders/benders_sequential/BendersSequential.cpp +++ b/src/cpp/benders/benders_sequential/BendersSequential.cpp @@ -17,9 +17,11 @@ * \param options : set of options fixed by the user */ -BendersSequential::BendersSequential(BendersBaseOptions const &options, - Logger logger, Writer writer) - : BendersBase(options, std::move(logger), std::move(writer)) {} +BendersSequential::BendersSequential( + BendersBaseOptions const &options, Logger logger, Writer writer, + std::shared_ptr mathLoggerDriver) + : BendersBase(options, std::move(logger), std::move(writer), + mathLoggerDriver) {} void BendersSequential::InitializeProblems() { MatchProblemToId(); diff --git a/src/cpp/benders/benders_sequential/include/BendersSequential.h b/src/cpp/benders/benders_sequential/include/BendersSequential.h index 0e7a90e99..141226e1e 100644 --- a/src/cpp/benders/benders_sequential/include/BendersSequential.h +++ b/src/cpp/benders/benders_sequential/include/BendersSequential.h @@ -11,8 +11,9 @@ */ class BendersSequential : public BendersBase { public: - explicit BendersSequential(BendersBaseOptions const &options, Logger logger, - Writer writer); + explicit BendersSequential( + BendersBaseOptions const &options, Logger logger, Writer writer, + std::shared_ptr mathLoggerDriver); virtual ~BendersSequential() = default; virtual void launch(); virtual void BuildCut(); diff --git a/src/cpp/benders/factories/BendersFactory.cpp b/src/cpp/benders/factories/BendersFactory.cpp index 35df81627..f42379567 100644 --- a/src/cpp/benders/factories/BendersFactory.cpp +++ b/src/cpp/benders/factories/BendersFactory.cpp @@ -16,7 +16,9 @@ int RunBenders(char** argv, const std::filesystem::path& options_file, mpi::environment& env, mpi::communicator& world, const BENDERSMETHOD& method) { // Read options, needed to have options.OUTPUTROOT + BendersLoggerBase benders_loggers; Logger logger; + std::shared_ptr math_log_driver; try { /* code */ @@ -34,12 +36,19 @@ int RunBenders(char** argv, const std::filesystem::path& options_file, auto log_reports_name = std::filesystem::path(options.OUTPUTROOT) / "reportbenders.txt"; + auto math_logs_file = + std::filesystem::path(options.OUTPUTROOT) / "benders_solver.log"; + Writer writer; if (world.rank() == 0) { - auto logger_factory = FileAndStdoutLoggerFactory(log_reports_name); + auto logger_factory = FileAndStdoutLoggerFactory( + log_reports_name, benders_options.EXPERT_LOGS); + auto math_log_factory = MathLoggerFactory( + method, benders_options.EXPERT_LOGS, math_logs_file); logger = logger_factory.get_logger(); + math_log_driver = math_log_factory.get_logger(); writer = build_json_writer(options.JSON_FILE, options.RESUME); if (Benders::StartUp startup; startup.StudyAlreadyAchievedCriterion(options, writer, logger)) @@ -47,24 +56,27 @@ int RunBenders(char** argv, const std::filesystem::path& options_file, } else { logger = build_void_logger(); writer = build_void_writer(); + auto math_log_factory = MathLoggerFactory(); + math_log_driver = math_log_factory.get_logger(); } - + benders_loggers.AddLogger(logger); + benders_loggers.AddLogger(math_log_driver); pBendersBase benders; if (method == BENDERSMETHOD::BENDERS) { benders = std::make_shared(benders_options, logger, writer, - env, world); + env, world, math_log_driver); } else if (method == BENDERSMETHOD::BENDERSBYBATCH) { - benders = std::make_shared(benders_options, logger, - writer, env, world); + benders = std::make_shared( + benders_options, logger, writer, env, world, math_log_driver); } else { auto err_msg = "Error only benders or benders-by-batch allowed!"; - logger->display_message(err_msg); + benders_loggers.display_message(err_msg); std::exit(1); } std::ostringstream oss_l = start_message(options, benders->BendersName()); oss_l << std::endl; - logger->display_message(oss_l.str()); - + benders_loggers.display_message(oss_l.str()); + benders->set_log_file(log_reports_name); writer->write_log_level(options.LOG_LEVEL); @@ -73,18 +85,18 @@ int RunBenders(char** argv, const std::filesystem::path& options_file, benders->launch(); std::stringstream str; str << "Optimization results available in : " << options.JSON_FILE; - logger->display_message(str.str()); + benders_loggers.display_message(str.str()); logger->log_total_duration(benders->execution_time()); } catch (std::exception& e) { std::ostringstream msg; msg << "error: " << e.what() << std::endl; - logger->display_message(msg.str()); + benders_loggers.display_message(msg.str()); mpi::environment::abort(1); return 1; } catch (...) { std::ostringstream msg; msg << "Exception of unknown type!" << std::endl; - logger->display_message(msg.str()); + benders_loggers.display_message(msg.str()); mpi::environment::abort(1); return 1; } diff --git a/src/cpp/benders/factories/include/BendersFactory.h b/src/cpp/benders/factories/include/BendersFactory.h index a3e310f52..db304e415 100644 --- a/src/cpp/benders/factories/include/BendersFactory.h +++ b/src/cpp/benders/factories/include/BendersFactory.h @@ -5,7 +5,7 @@ #include "BendersSequential.h" #include "ILogger.h" #include "OutputWriter.h" -enum class BENDERSMETHOD { BENDERS, BENDERSBYBATCH, MERGEMPS }; +#include "common.h" class BendersMainFactory { private: diff --git a/src/cpp/benders/factories/include/LoggerFactories.h b/src/cpp/benders/factories/include/LoggerFactories.h index fcd3086ed..82bb4fe41 100644 --- a/src/cpp/benders/factories/include/LoggerFactories.h +++ b/src/cpp/benders/factories/include/LoggerFactories.h @@ -4,9 +4,11 @@ #include +#include "BendersFactory.h" #include "ILogger.h" #include "SimulationOptions.h" #include "logger/Master.h" +#include "logger/MathLogger.h" #include "logger/UserFile.h" Logger build_void_logger(); @@ -19,17 +21,46 @@ class FileAndStdoutLoggerFactory { public: explicit FileAndStdoutLoggerFactory( - const std::filesystem::path &report_file_path_string) { + const std::filesystem::path &report_file_path_string, + bool expert_log_at_console) { auto masterLogger = std::make_shared(); auto user_file = std::make_shared(report_file_path_string); - - auto loggerUser = std::make_shared(std::cout); masterLogger->addLogger(user_file); - masterLogger->addLogger(loggerUser); + + if (!expert_log_at_console) { + auto loggerUser = std::make_shared(std::cout); + masterLogger->addLogger(loggerUser); + } + logger = masterLogger; } - inline Logger get_logger() const { return logger; } }; + +class MathLoggerFactory { + private: + MathLoggerDriver math_logger_driver; + + public: + explicit MathLoggerFactory( + const BENDERSMETHOD &method, bool console_log, + const std::filesystem::path &math_logs_file_path = "") { + if (math_logs_file_path != "") { + auto math_logger_file = + std::make_shared(method, math_logs_file_path); + math_logger_driver.add_logger(math_logger_file); + } + + if (console_log) { + auto math_logger_ostream = std::make_shared(method); + + math_logger_driver.add_logger(math_logger_ostream); + } + } + explicit MathLoggerFactory() = default; + std::shared_ptr get_logger() { + return std::make_shared(math_logger_driver); + } + }; #endif // ANTARESXPANSION_LOGGERFACTORIES_H diff --git a/src/cpp/benders/logger/CMakeLists.txt b/src/cpp/benders/logger/CMakeLists.txt index cd0bf4f45..d322c811a 100644 --- a/src/cpp/benders/logger/CMakeLists.txt +++ b/src/cpp/benders/logger/CMakeLists.txt @@ -5,7 +5,8 @@ add_library (logger_lib User.cpp UserFile.cpp CandidateLog.cpp - IterationResultLog.cpp ) + IterationResultLog.cpp + MathLogger.cpp ) target_link_libraries (logger_lib PUBLIC diff --git a/src/cpp/benders/logger/Master.cpp b/src/cpp/benders/logger/Master.cpp index 410edf8f7..d2e0236b1 100644 --- a/src/cpp/benders/logger/Master.cpp +++ b/src/cpp/benders/logger/Master.cpp @@ -106,9 +106,9 @@ void Master::LogAtSwitchToInteger() { } } -void Master::cumulative_number_of_sub_problem_resolved(int number) { +void Master::cumulative_number_of_sub_problem_solved(int number) { for (auto logger : _loggers) { - logger->cumulative_number_of_sub_problem_resolved(number); + logger->cumulative_number_of_sub_problem_solved(number); } } diff --git a/src/cpp/benders/logger/MathLogger.cpp b/src/cpp/benders/logger/MathLogger.cpp new file mode 100644 index 000000000..919529a6c --- /dev/null +++ b/src/cpp/benders/logger/MathLogger.cpp @@ -0,0 +1,104 @@ +#include "logger/MathLogger.h" + +#include +#include + +double getDurationNotDoingMasterOrSubproblems(double interation, double master, + double subproblems) { + return interation - master - subproblems; +} + +void MathLoggerBase::setHeadersList() { + auto type = HeadersType(); + HeadersManager headers_manager(type, BENDERSMETHOD::BENDERS); + MathLogger::setHeadersList(headers_manager.headers_list); +} + +void MathLogger::setHeadersList(const std::vector& headers) { + headers_.clear(); + headers_ = headers; +} + +void MathLoggerBase::Print(const CurrentIterationData& data) { + auto type = HeadersType(); + + LogsDestination() << data.it; + LogsDestination() << std::scientific << std::setprecision(10) << data.lb; + LogsDestination() << std::scientific << std::setprecision(10) << data.ub; + LogsDestination() << std::scientific << std::setprecision(10) << data.best_ub; + LogsDestination() << std::scientific << std::setprecision(2) + << data.best_ub - data.lb; + LogsDestination() << std::scientific << std::setprecision(2) + << (data.best_ub - data.lb) / data.best_ub; + LogsDestination() << data.min_simplexiter; + LogsDestination() << data.max_simplexiter; + if (type == HEADERSTYPE::LONG) { + LogsDestination() << data.number_of_subproblem_solved; + LogsDestination() << data.cumulative_number_of_subproblem_solved; + } + + // LogsDestination() << data.deletedcut; + LogsDestination() << std::setprecision(2) << data.elapsed_time; + LogsDestination() << std::setprecision(2) << data.timer_master; + if (type == HEADERSTYPE::LONG) { + LogsDestination() << std::setprecision(2) + << data.subproblems_cumulative_cputime; + } + LogsDestination() << std::setprecision(2) << data.subproblems_walltime; + if (type == HEADERSTYPE::LONG) { + LogsDestination() << std::setprecision(2) + << getDurationNotDoingMasterOrSubproblems( + data.elapsed_time, data.timer_master, + data.subproblems_walltime); + } + LogsDestination() << std::endl; +} + +void MathLoggerBendersByBatch::setHeadersList() { + auto type = HeadersType(); + HeadersManager headers_manager(type, BENDERSMETHOD::BENDERSBYBATCH); + + MathLogger::setHeadersList(headers_manager.headers_list); +} + +void MathLoggerBendersByBatch::Print(const CurrentIterationData& data) { + auto type = HeadersType(); + + LogsDestination() << data.it; + LogsDestination() << std::scientific << std::setprecision(10) << data.lb; + LogsDestination() << data.min_simplexiter; + LogsDestination() << data.max_simplexiter; + LogsDestination() << data.number_of_subproblem_solved; + + if (type == HEADERSTYPE::LONG) { + LogsDestination() << data.cumulative_number_of_subproblem_solved; + } + LogsDestination() << std::setprecision(2) << data.elapsed_time; + LogsDestination() << std::setprecision(2) << data.timer_master; + if (type == HEADERSTYPE::LONG) { + LogsDestination() << std::setprecision(2) + << data.subproblems_cumulative_cputime; + } + LogsDestination() << std::setprecision(2) << data.subproblems_walltime; + if (type == HEADERSTYPE::LONG) { + LogsDestination() << std::setprecision(2) + << getDurationNotDoingMasterOrSubproblems( + data.elapsed_time, data.timer_master, + data.subproblems_walltime); + } + + LogsDestination() << std::endl; +} + +MathLoggerFile::MathLoggerFile(const BENDERSMETHOD& method, + const std::filesystem::path& filename, + std::streamsize width) + : MathLoggerImplementation(method, &file_stream_, width, + HEADERSTYPE::LONG) { + // TODO restart case????????????? + file_stream_.open(filename, std::ofstream::out); + if (file_stream_.fail()) { + std::cerr << PrefixMessage(LogUtils::LOGLEVEL::ERR, MATHLOGGERCONTEXT) + << "Invalid file name passed as parameter" << std::endl; + } +} \ No newline at end of file diff --git a/src/cpp/benders/logger/User.cpp b/src/cpp/benders/logger/User.cpp index 84ce7f4e0..087899a4a 100644 --- a/src/cpp/benders/logger/User.cpp +++ b/src/cpp/benders/logger/User.cpp @@ -121,7 +121,7 @@ void User::LogAtSwitchToInteger() { << "--- Relaxed gap reached, switch master formulation to integer" << std::endl; } -void User::cumulative_number_of_sub_problem_resolved(int number) { +void User::cumulative_number_of_sub_problem_solved(int number) { _stream << PrefixMessage(LogUtils::LOGLEVEL::INFO, CONTEXT) << indent_1 << "cumulative number of subproblem resolutions: " << number << std::endl; diff --git a/src/cpp/benders/logger/UserFile.cpp b/src/cpp/benders/logger/UserFile.cpp index b856f72d7..ba72d62f1 100644 --- a/src/cpp/benders/logger/UserFile.cpp +++ b/src/cpp/benders/logger/UserFile.cpp @@ -144,7 +144,7 @@ void UserFile::LogAtSwitchToInteger() { << std::endl; _file.flush(); } -void UserFile::cumulative_number_of_sub_problem_resolved(int number) { +void UserFile::cumulative_number_of_sub_problem_solved(int number) { _file << PrefixMessage(LogUtils::LOGLEVEL::INFO, CONTEXT) << indent_1 << "cumulative number of call to solver (only for subproblems) : " << number << std::endl; diff --git a/src/cpp/benders/logger/include/logger/Master.h b/src/cpp/benders/logger/include/logger/Master.h index 65110950e..ff4b44aa7 100644 --- a/src/cpp/benders/logger/include/logger/Master.h +++ b/src/cpp/benders/logger/include/logger/Master.h @@ -56,7 +56,7 @@ class Master : public ILogger { const LogData &best_iterations_data) override; void LogAtInitialRelaxation() override; void LogAtSwitchToInteger() override; - void cumulative_number_of_sub_problem_resolved(int number) override; + void cumulative_number_of_sub_problem_solved(int number) override; private: std::list> _loggers; diff --git a/src/cpp/benders/logger/include/logger/MathLogger.h b/src/cpp/benders/logger/include/logger/MathLogger.h new file mode 100644 index 000000000..0b9b8bcd9 --- /dev/null +++ b/src/cpp/benders/logger/include/logger/MathLogger.h @@ -0,0 +1,23 @@ + +#pragma once +#include + +#include "BendersMathLogger.h" +#include "LoggerUtils.h" + +class MathLoggerFile : public MathLoggerImplementation { + public: + explicit MathLoggerFile(const BENDERSMETHOD& method, + const std::filesystem::path& log_file, + std::streamsize width = 45); + + private: + std::ofstream file_stream_; +}; +class MathLoggerOstream : public MathLoggerImplementation { + public: + explicit MathLoggerOstream(const BENDERSMETHOD& method, + std::streamsize width = 20) + : MathLoggerImplementation(method, &std::cout, width, + HEADERSTYPE::SHORT) {} +}; diff --git a/src/cpp/benders/logger/include/logger/User.h b/src/cpp/benders/logger/include/logger/User.h index c7218cd7f..721825682 100644 --- a/src/cpp/benders/logger/include/logger/User.h +++ b/src/cpp/benders/logger/include/logger/User.h @@ -48,7 +48,7 @@ class User : public ILogger { const LogData &best_iterations_data) override; void LogAtInitialRelaxation() override; void LogAtSwitchToInteger() override; - void cumulative_number_of_sub_problem_resolved(int number) override; + void cumulative_number_of_sub_problem_solved(int number) override; private: std::ostream &_stream; diff --git a/src/cpp/benders/logger/include/logger/UserFile.h b/src/cpp/benders/logger/include/logger/UserFile.h index d8f9319a8..a0c272e5b 100644 --- a/src/cpp/benders/logger/include/logger/UserFile.h +++ b/src/cpp/benders/logger/include/logger/UserFile.h @@ -47,7 +47,7 @@ class UserFile : public ILogger { const LogData &best_iterations_data) override; void LogAtInitialRelaxation() override; void LogAtSwitchToInteger() override; - void cumulative_number_of_sub_problem_resolved(int number) override; + void cumulative_number_of_sub_problem_solved(int number) override; private: std::ofstream _file; diff --git a/src/cpp/xpansion_interfaces/ILogger.h b/src/cpp/xpansion_interfaces/ILogger.h index 1d829e64d..acf1bbaa4 100644 --- a/src/cpp/xpansion_interfaces/ILogger.h +++ b/src/cpp/xpansion_interfaces/ILogger.h @@ -64,7 +64,24 @@ struct LogData { double subproblem_time; int cumulative_number_of_subproblem_resolved; }; -class ILogger { +struct ILoggerBenders { + virtual void display_message(const std::string &str) = 0; +}; + +struct BendersLoggerBase : public ILoggerBenders { + void display_message(const std::string &str) override { + for (auto logger : loggers) { + logger->display_message(str); + } + } + void AddLogger(std::shared_ptr logger) { + loggers.push_back(logger); + } + + private: + std::vector> loggers; +}; +class ILogger : public ILoggerBenders { public: virtual ~ILogger() = default; @@ -91,7 +108,7 @@ class ILogger { const LogData &best_iterations_data) = 0; virtual void LogAtInitialRelaxation() = 0; virtual void LogAtSwitchToInteger() = 0; - virtual void cumulative_number_of_sub_problem_resolved(int number) = 0; + virtual void cumulative_number_of_sub_problem_solved(int number) = 0; const std::string CONTEXT = "Benders"; }; diff --git a/src/python/antares_xpansion/config_loader.py b/src/python/antares_xpansion/config_loader.py index d4b560a3e..dfc622206 100644 --- a/src/python/antares_xpansion/config_loader.py +++ b/src/python/antares_xpansion/config_loader.py @@ -14,7 +14,7 @@ from antares_xpansion.chronicles_checker import ChronicleChecker from antares_xpansion.logger import step_logger from antares_xpansion.general_data_reader import GeneralDataIniReader -from antares_xpansion.input_checker import check_candidates_file, check_options +from antares_xpansion.input_checker import check_candidates_file, check_options, str_to_bool from antares_xpansion.launcher_options_default_value import LauncherOptionsDefaultValues from antares_xpansion.launcher_options_keys import LauncherOptionsKeys from antares_xpansion.optimisation_keys import OptimisationKeys @@ -333,6 +333,19 @@ def get_batch_size(self): return int(batch_size_str) + def get_expert_logs(self): + """ + return the expert_log option from settings file + """ + expert_logs = self.options.get( + "expert_logs", + self._config.settings_default["expert_logs"], + ) + if (isinstance(expert_logs, bool)): + return expert_logs + else: + return str_to_bool(self._config.settings_default["expert_logs"])[1] + def additional_constraints(self): """ returns path to additional constraints file @@ -485,6 +498,8 @@ def _set_options_for_benders_solver(self): options_values["LAST_MASTER_BASIS"] = self._config.LAST_MASTER_BASIS options_values[OptimisationKeys.batch_size_key() ] = self.get_batch_size() + options_values[OptimisationKeys.expert_logs_key() + ] = self.get_expert_logs() # generate options file for the solver with open(self.options_file_path(), "w") as options_file: json.dump(options_values, options_file, indent=4) @@ -537,7 +552,7 @@ def update_last_study_with_sensitivity_results(self): def is_antares_study_output(self, study: Path): _, ext = os.path.splitext(study) - return ext == ".zip" or os.path.isdir(study) and '-Xpansion' not in study.name + return ext == ".zip" or (os.path.isdir(study) and '-Xpansion' in study.name) def last_modified_study(self, root_dir:Path)-> Path: list_dir = os.listdir(root_dir) diff --git a/src/python/antares_xpansion/input_checker.py b/src/python/antares_xpansion/input_checker.py index cc248a7e2..0bc3a125f 100644 --- a/src/python/antares_xpansion/input_checker.py +++ b/src/python/antares_xpansion/input_checker.py @@ -344,10 +344,22 @@ class NotHandledOption(Exception): class NotHandledValue(Exception): pass +# return ->tuple[is_a_bool: bool, result: bool] + + +def str_to_bool(my_str: str) -> tuple[bool, bool]: + if my_str in ["true", "True", "TRUE", "1"]: + return (True, True) + elif my_str in ["false", "False", "False", "0"]: + return (True, False) + else: + return (False, False) type_str = str type_int = int type_float = float +type_bool = bool + # "option": (type, legal_value(s)) options_types_and_legal_values = { @@ -364,6 +376,7 @@ class NotHandledValue(Exception): "log_level": (type_int, ["0", "1", "2", "3"]), "separation_parameter": (type_float, None), "batch_size": (type_int, None), + "expert_logs": (type_bool, None), } @@ -408,6 +421,14 @@ def _check_setting_option_type(option, value): logger.error( 'check_setting_option_type: Illegal %s option in type, integer is expected .' % option) return False + elif option_type == type_bool: + [is_a_bool, ret] = str_to_bool(value) + if is_a_bool: + return True + else: + logger.error( + 'check_setting_option_type: Illegal %s option in type, boolean is expected .' % option) + return False return isinstance(value, type_str) @@ -440,6 +461,10 @@ class BatchSizeValueError(Exception): pass +class ExpertLogsValueError(Exception): + pass + + def check_options(options): """ checks that a settings file related to an XpansionDriver has the correct format @@ -498,6 +523,8 @@ def _check_batch_size(value) -> bool: raise BatchSizeValueError + + def _check_separation(value) -> bool: if 0 <= float(value) <= 1: return True @@ -560,6 +587,10 @@ def _check_setting_option_value(option, value): elif option == "batch_size": return _check_batch_size(value) + elif option == "expert_logs": + return str_to_bool(value)[0] + + logger.error( 'check_candidate_option_value: Illegal value %s for option %s' % (value, option)) sys.exit(1) diff --git a/src/python/antares_xpansion/optimisation_keys.py b/src/python/antares_xpansion/optimisation_keys.py index fcc6d3a6a..f79f14c5f 100644 --- a/src/python/antares_xpansion/optimisation_keys.py +++ b/src/python/antares_xpansion/optimisation_keys.py @@ -94,3 +94,7 @@ def separation_key(): @staticmethod def batch_size_key(): return "BATCH_SIZE" + + @staticmethod + def expert_logs_key(): + return "EXPERT_LOGS" diff --git a/src/python/antares_xpansion/xpansionConfig.py b/src/python/antares_xpansion/xpansionConfig.py index 01d31e7b7..cf471641b 100644 --- a/src/python/antares_xpansion/xpansionConfig.py +++ b/src/python/antares_xpansion/xpansionConfig.py @@ -160,6 +160,7 @@ def _set_default_settings(self): "log_level": "0", "separation_parameter": "0.5", "batch_size": "0", + "expert_logs": False, } def _set_default_options(self): @@ -180,6 +181,7 @@ def _set_default_options(self): OptimisationKeys.bound_alpha_key(): self.bound_alpha_default_value(), OptimisationKeys.separation_key(): self.separation_default_value(), OptimisationKeys.batch_size_key(): self.batch_size_default_value(), + OptimisationKeys.expert_logs_key(): self.expert_logs_default_value(), } def bound_alpha_default_value(self): @@ -233,6 +235,9 @@ def separation_default_value(self): def batch_size_default_value(self): return "0" + def expert_logs_default_value(self): + return False + def _get_config_values(self): self.default_install_dir = self.config_parameters.default_install_dir diff --git a/tests/cpp/TestDoubles/LoggerStub.h b/tests/cpp/TestDoubles/LoggerStub.h index 395407674..a0a965c31 100644 --- a/tests/cpp/TestDoubles/LoggerStub.h +++ b/tests/cpp/TestDoubles/LoggerStub.h @@ -30,5 +30,5 @@ class LoggerNOOPStub : public ILogger { const LogData& best_iterations_data) override {} void LogAtInitialRelaxation() override {} void LogAtSwitchToInteger() override {} - void cumulative_number_of_sub_problem_resolved(int number) override {} + void cumulative_number_of_sub_problem_solved(int number) override {} }; \ No newline at end of file diff --git a/tests/cpp/benders/benders_sequential_test.cpp b/tests/cpp/benders/benders_sequential_test.cpp index acf7f2c29..eeafff567 100644 --- a/tests/cpp/benders/benders_sequential_test.cpp +++ b/tests/cpp/benders/benders_sequential_test.cpp @@ -42,9 +42,10 @@ class BendersSequentialDouble : public BendersSequential { bool _setDataPreRelaxationCall = false; bool _setDataPostRelaxationCall = false; - explicit BendersSequentialDouble(BendersBaseOptions const &options, - Logger &logger, Writer writer) - : BendersSequential(options, logger, writer){}; + explicit BendersSequentialDouble( + BendersBaseOptions const &options, Logger &logger, Writer writer, + std::shared_ptr mathLoggerDriver) + : BendersSequential(options, logger, writer, mathLoggerDriver){}; void init_data() override { BendersBase::init_data(); @@ -131,6 +132,7 @@ class BendersSequentialDouble : public BendersSequential { class BendersSequentialTest : public ::testing::Test { public: Logger logger; + std::shared_ptr mathLoggerDriver; Writer writer; const std::filesystem::path data_test_dir = "data_test"; const std::filesystem::path mps_dir = data_test_dir / "mps"; @@ -196,7 +198,7 @@ class BendersSequentialTest : public ::testing::Test { double sep_param) { BendersBaseOptions options = init_benders_options( master_formulation, max_iter, relaxed_gap, sep_param); - return BendersSequentialDouble(options, logger, writer); + return BendersSequentialDouble(options, logger, writer, mathLoggerDriver); } std::vector get_nb_units_col_types( diff --git a/tests/cpp/logger/logger_test.cpp b/tests/cpp/logger/logger_test.cpp index 78d721a29..faaa8dc4c 100644 --- a/tests/cpp/logger/logger_test.cpp +++ b/tests/cpp/logger/logger_test.cpp @@ -443,7 +443,7 @@ TEST_F(UserLoggerTest, EndLog) { TEST_F(UserLoggerTest, CumulativeNumberOfSubProblemResolved) { auto number(9150); - _logger.cumulative_number_of_sub_problem_resolved(number); + _logger.cumulative_number_of_sub_problem_solved(number); auto logWithoutPrefix = RemovePrefixFromMessage(_stream); std::stringstream expected; expected << " " << indent_1 @@ -619,7 +619,7 @@ class SimpleLoggerMock : public ILogger { void LogAtInitialRelaxation() { _initialRelaxationCall = true; } void LogAtSwitchToInteger() { _switchToIntegerCall = true; } - void cumulative_number_of_sub_problem_resolved(int number) { + void cumulative_number_of_sub_problem_solved(int number) { _cumulativeNumberOfSubProblemResolved = true; } @@ -682,7 +682,7 @@ TEST_F(MasterLoggerTest, EndLog) { TEST_F(MasterLoggerTest, CumulativeNumberOfSubProblemResolved) { LogData logData; - _master.cumulative_number_of_sub_problem_resolved(39); + _master.cumulative_number_of_sub_problem_solved(39); ASSERT_TRUE(_logger->_cumulativeNumberOfSubProblemResolved); ASSERT_TRUE(_logger2->_cumulativeNumberOfSubProblemResolved); }