From 12bf8f67f5f801279e464a88b4361180a4c62f19 Mon Sep 17 00:00:00 2001 From: abdoulbari zakir <32519851+a-zakir@users.noreply.github.com> Date: Mon, 18 Mar 2024 17:20:11 +0100 Subject: [PATCH 1/9] [ANT-1224] Preparing loop around benders solver (#739) steps: - [x] create a storage struct for cuts data - [x] interface and its implementation(s) to save cuts data - [x] re-use cuts - [x] external loop criterion check - [x] add external loop constraint - [x] add "mathematical" logs for external loop (think about data to print) - [x] improve operational logs with external loop - [x] Computation of initial lambda_max by solving investment free problem + check criterion -> raise warning / exception if not satisfied at this point - [ ] Handle infeasibility / errors / ... - [x] Write test #706 --- .../external_loop_test/expansion/.gitignore | 4 + data_test/external_loop_test/lp/master.mps | 17 ++ .../lp/master_last_basis.bss | 5 + data_test/external_loop_test/lp/options.json | 29 ++ data_test/external_loop_test/lp/structure.txt | 2 + .../external_loop_test/lp/subproblem.mps | 88 ++++++ src/cpp/benders/CMakeLists.txt | 1 + .../benders_by_batch/BendersByBatch.cpp | 24 +- src/cpp/benders/benders_core/BendersBase.cpp | 238 ++++++++++++----- .../benders_core/BendersMathLogger.cpp | 211 ++++++++++++++- .../benders_core/SimulationOptions.cpp | 8 +- .../benders/benders_core/SubproblemWorker.cpp | 17 ++ src/cpp/benders/benders_core/Worker.cpp | 28 +- .../benders_core/include/BendersBase.h | 73 ++++- .../benders_core/include/BendersMathLogger.h | 72 +++-- .../include/BendersStructsDatas.h | 21 +- .../benders_core/include/SimulationOptions.h | 1 + .../include/SimulationOptions.hxx | 10 + .../benders_core/include/SubproblemCut.h | 28 +- .../benders_core/include/SubproblemWorker.h | 1 + src/cpp/benders/benders_core/include/Worker.h | 19 ++ src/cpp/benders/benders_core/include/common.h | 13 +- src/cpp/benders/benders_mpi/BendersMPI.cpp | 40 ++- src/cpp/benders/external_loop/CMakeLists.txt | 40 +++ .../benders/external_loop/CutsManagement.cpp | 7 + .../external_loop/MasterUpdateBase.cpp | 112 ++++++++ src/cpp/benders/external_loop/OuterLoop.cpp | 90 +++++++ .../external_loop/OuterloopCriterion.cpp | 49 ++++ .../external_loop/include/CutsManagement.h | 18 ++ .../external_loop/include/MasterUpdate.h | 37 +++ .../benders/external_loop/include/OuterLoop.h | 25 ++ .../include/OuterLoopCriterion.h | 39 +++ src/cpp/benders/factories/BendersFactory.cpp | 245 +++++++++++------ src/cpp/benders/factories/CMakeLists.txt | 1 + .../factories/include/BendersFactory.h | 6 +- src/cpp/benders/logger/Master.cpp | 11 + src/cpp/benders/logger/MathLogger.cpp | 84 +----- src/cpp/benders/logger/User.cpp | 14 + src/cpp/benders/logger/UserFile.cpp | 15 ++ .../benders/logger/include/logger/Master.h | 3 + .../logger/include/logger/MathLogger.h | 5 + src/cpp/benders/logger/include/logger/User.h | 3 + .../benders/logger/include/logger/UserFile.h | 4 + src/cpp/exe/CMakeLists.txt | 1 + src/cpp/exe/ExtLoop/CMakeLists.txt | 33 +++ src/cpp/exe/ExtLoop/main.cpp | 9 + src/cpp/helpers/solver_utils.cc | 16 +- src/cpp/helpers/solver_utils.h | 5 +- src/cpp/lpnamer/helper/ColumnToChange.h | 3 +- .../input_reader/VariableFileReader.cpp | 4 +- .../lpnamer/input_reader/VariableFileReader.h | 2 +- src/cpp/lpnamer/model/Problem.h | 14 +- src/cpp/multisolver_interface/SolverCbc.cpp | 36 ++- src/cpp/multisolver_interface/SolverCbc.h | 7 +- src/cpp/multisolver_interface/SolverClp.cpp | 33 ++- src/cpp/multisolver_interface/SolverClp.h | 7 +- .../multisolver_interface/SolverXpress.cpp | 33 ++- src/cpp/multisolver_interface/SolverXpress.h | 7 +- .../multisolver_interface/SolverAbstract.h | 16 +- src/cpp/xpansion_interfaces/ILogger.h | 18 ++ tests/cpp/CMakeLists.txt | 1 + tests/cpp/TestDoubles/LoggerStub.h | 6 + tests/cpp/ext_loop/CMakeLists.txt | 21 ++ tests/cpp/ext_loop/ext_loop_test.cpp | 250 ++++++++++++++++++ tests/cpp/logger/logger_test.cpp | 27 +- tests/cpp/lp_namer/NOOPSolver.h | 7 +- 66 files changed, 1991 insertions(+), 323 deletions(-) create mode 100644 data_test/external_loop_test/expansion/.gitignore create mode 100644 data_test/external_loop_test/lp/master.mps create mode 100644 data_test/external_loop_test/lp/master_last_basis.bss create mode 100644 data_test/external_loop_test/lp/options.json create mode 100644 data_test/external_loop_test/lp/structure.txt create mode 100644 data_test/external_loop_test/lp/subproblem.mps create mode 100644 src/cpp/benders/external_loop/CMakeLists.txt create mode 100644 src/cpp/benders/external_loop/CutsManagement.cpp create mode 100644 src/cpp/benders/external_loop/MasterUpdateBase.cpp create mode 100644 src/cpp/benders/external_loop/OuterLoop.cpp create mode 100644 src/cpp/benders/external_loop/OuterloopCriterion.cpp create mode 100644 src/cpp/benders/external_loop/include/CutsManagement.h create mode 100644 src/cpp/benders/external_loop/include/MasterUpdate.h create mode 100644 src/cpp/benders/external_loop/include/OuterLoop.h create mode 100644 src/cpp/benders/external_loop/include/OuterLoopCriterion.h create mode 100644 src/cpp/exe/ExtLoop/CMakeLists.txt create mode 100644 src/cpp/exe/ExtLoop/main.cpp create mode 100644 tests/cpp/ext_loop/CMakeLists.txt create mode 100644 tests/cpp/ext_loop/ext_loop_test.cpp diff --git a/data_test/external_loop_test/expansion/.gitignore b/data_test/external_loop_test/expansion/.gitignore new file mode 100644 index 000000000..5e7d2734c --- /dev/null +++ b/data_test/external_loop_test/expansion/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/data_test/external_loop_test/lp/master.mps b/data_test/external_loop_test/lp/master.mps new file mode 100644 index 000000000..cb0088b71 --- /dev/null +++ b/data_test/external_loop_test/lp/master.mps @@ -0,0 +1,17 @@ +* Generated by MPModelProtoExporter +* Name : +* Format : Free +* Constraints : 0 +* Variables : 1 +* Binary : 0 +* Integer : 0 +* Continuous : 1 +NAME +ROWS + N COST +COLUMNS + G_p_max_0_0 COST 20 +RHS +BOUNDS + UP BOUND G_p_max_0_0 10 +ENDATA diff --git a/data_test/external_loop_test/lp/master_last_basis.bss b/data_test/external_loop_test/lp/master_last_basis.bss new file mode 100644 index 000000000..f632a4bee --- /dev/null +++ b/data_test/external_loop_test/lp/master_last_basis.bss @@ -0,0 +1,5 @@ +NAME + LL G_p_max_0_0 0 + XU alpha R1 19 0 + XL alpha_0 R2 19 -19 +ENDATA diff --git a/data_test/external_loop_test/lp/options.json b/data_test/external_loop_test/lp/options.json new file mode 100644 index 000000000..13a960d34 --- /dev/null +++ b/data_test/external_loop_test/lp/options.json @@ -0,0 +1,29 @@ +{ + "MAX_ITERATIONS": -1, + "ABSOLUTE_GAP": 1e-04, + "RELATIVE_GAP": 1e-06, + "RELAXED_GAP": 1e-05, + "AGGREGATION": false, + "OUTPUTROOT": "data_test/external_loop_test/lp/", + "TRACE": true, + "SLAVE_WEIGHT": "CONSTANT", + "SLAVE_WEIGHT_VALUE": 3, + "MASTER_NAME": "master", + "STRUCTURE_FILE": "data_test/external_loop_test/lp/structure.txt", + "INPUTROOT": "data_test/external_loop_test/lp/", + "CSV_NAME": "benders_output_trace", + "BOUND_ALPHA": true, + "SEPARATION_PARAM": 0.5, + "BATCH_SIZE": 0, + "JSON_FILE":"data_test/external_loop_test/expansion/out.json", + "LAST_ITERATION_JSON_FILE":"data_test/external_loop_test/expansion/last_iteration.json", + "MASTER_FORMULATION": "integer", + "SOLVER_NAME": "XPRESS", + "TIME_LIMIT": 1000000000000.0, + "LOG_LEVEL": 1, + "LAST_MASTER_MPS": "master_last_iteration", + "LAST_MASTER_BASIS": "master_last_basis.bss", + "EXT_LOOP_CRITERION_VALUE": 3.0, + "EXT_LOOP_CRITERION_TOLERANCE": 1e-1, + "EXT_LOOP_CRITERION_COUNT_THRESHOLD": 1e-1 +} diff --git a/data_test/external_loop_test/lp/structure.txt b/data_test/external_loop_test/lp/structure.txt new file mode 100644 index 000000000..838d690ea --- /dev/null +++ b/data_test/external_loop_test/lp/structure.txt @@ -0,0 +1,2 @@ + master G_p_max_0_0 0 + subproblem.mps G_p_max_0_0 24 diff --git a/data_test/external_loop_test/lp/subproblem.mps b/data_test/external_loop_test/lp/subproblem.mps new file mode 100644 index 000000000..e28608e1c --- /dev/null +++ b/data_test/external_loop_test/lp/subproblem.mps @@ -0,0 +1,88 @@ +* Generated by MPModelProtoExporter +* Name : +* Format : Free +* Constraints : 10 +* Variables : 25 +* Binary : 0 +* Integer : 0 +* Continuous : 25 +NAME +ROWS + N COST + E N0_Balance + E N0_Balance_1 + E N1_Balance + E N1_Balance_2 + E N2_Balance + E N2_Balance_3 + E N3_Balance + E N3_Balance_4 + L G_Max_generation + L G_Max_generation_5 +COLUMNS + N0_NegativeUnsuppliedEnergy_0_0 COST 0.5 N0_Balance -1 + N0_NegativeUnsuppliedEnergy_0_1 COST 0.5 N0_Balance_1 -1 + PositiveUnsuppliedEnergy::N0::0::0 COST 5 N0_Balance 1 + PositiveUnsuppliedEnergy::N0::0::1 COST 5 N0_Balance_1 1 + N1_NegativeUnsuppliedEnergy_0_0 COST 0.5 N1_Balance -1 + N1_NegativeUnsuppliedEnergy_0_1 COST 0.5 N1_Balance_2 -1 + PositiveUnsuppliedEnergy::N1::0::0 COST 5 N1_Balance 1 + PositiveUnsuppliedEnergy::N1::0::1 COST 5 N1_Balance_2 1 + N2_NegativeUnsuppliedEnergy_0_0 COST 0.5 N2_Balance -1 + N2_NegativeUnsuppliedEnergy_0_1 COST 0.5 N2_Balance_3 -1 + PositiveUnsuppliedEnergy::N2::0::0 COST 5 N2_Balance 1 + PositiveUnsuppliedEnergy::N2::0::1 COST 5 N2_Balance_3 1 + N3_NegativeUnsuppliedEnergy_0_0 COST 0.5 N3_Balance -1 + N3_NegativeUnsuppliedEnergy_0_1 COST 0.5 N3_Balance_4 -1 + PositiveUnsuppliedEnergy::N3::0::0 COST 5 N3_Balance 1 + PositiveUnsuppliedEnergy::N3::0::1 COST 5 N3_Balance_4 1 + L01_flow_0_0 COST 1 N0_Balance -1 + L01_flow_0_0 N1_Balance 1 + L01_flow_0_1 COST 1 N0_Balance_1 -1 + L01_flow_0_1 N1_Balance_2 1 + L02_flow_0_0 COST 0.5 N0_Balance -1 + L02_flow_0_0 N2_Balance 1 + L02_flow_0_1 COST 0.5 N0_Balance_1 -1 + L02_flow_0_1 N2_Balance_3 1 + L03_flow_0_0 COST 1.5 N0_Balance -1 + L03_flow_0_0 N3_Balance 1 + L03_flow_0_1 COST 1.5 N0_Balance_1 -1 + L03_flow_0_1 N3_Balance_4 1 + G_generation_0_0 COST 0.5 N0_Balance 1 + G_generation_0_0 G_Max_generation 1 + G_generation_0_1 COST 0.5 N0_Balance_1 1 + G_generation_0_1 G_Max_generation_5 1 + G_p_max_0_0 G_Max_generation -1 G_Max_generation_5 -1 +RHS + RHS N0_Balance 0 N0_Balance_1 0 + RHS N1_Balance 1 N1_Balance_2 3 + RHS N2_Balance 3 N2_Balance_3 3 + RHS N3_Balance 0 N3_Balance_4 3 + RHS G_Max_generation 1 G_Max_generation_5 1 +BOUNDS + PL BOUND N0_NegativeUnsuppliedEnergy_0_0 + PL BOUND N0_NegativeUnsuppliedEnergy_0_1 + PL BOUND PositiveUnsuppliedEnergy::N0::0::0 + PL BOUND PositiveUnsuppliedEnergy::N0::0::1 + PL BOUND N1_NegativeUnsuppliedEnergy_0_0 + PL BOUND N1_NegativeUnsuppliedEnergy_0_1 + PL BOUND PositiveUnsuppliedEnergy::N1::0::0 + PL BOUND PositiveUnsuppliedEnergy::N1::0::1 + PL BOUND N2_NegativeUnsuppliedEnergy_0_0 + PL BOUND N2_NegativeUnsuppliedEnergy_0_1 + PL BOUND PositiveUnsuppliedEnergy::N2::0::0 + PL BOUND PositiveUnsuppliedEnergy::N2::0::1 + PL BOUND N3_NegativeUnsuppliedEnergy_0_0 + PL BOUND N3_NegativeUnsuppliedEnergy_0_1 + PL BOUND PositiveUnsuppliedEnergy::N3::0::0 + PL BOUND PositiveUnsuppliedEnergy::N3::0::1 + UP BOUND L01_flow_0_0 100 + UP BOUND L01_flow_0_1 100 + UP BOUND L02_flow_0_0 100 + UP BOUND L02_flow_0_1 100 + UP BOUND L03_flow_0_0 100 + UP BOUND L03_flow_0_1 100 + PL BOUND G_generation_0_0 + PL BOUND G_generation_0_1 + UP BOUND G_p_max_0_0 10 +ENDATA diff --git a/src/cpp/benders/CMakeLists.txt b/src/cpp/benders/CMakeLists.txt index e306bd7b1..adea040ed 100644 --- a/src/cpp/benders/CMakeLists.txt +++ b/src/cpp/benders/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/benders_core") add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/logger") add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/benders_sequential") add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/benders_by_batch") +add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/external_loop") add_subdirectory ("${CMAKE_CURRENT_SOURCE_DIR}/benders_mpi") diff --git a/src/cpp/benders/benders_by_batch/BendersByBatch.cpp b/src/cpp/benders/benders_by_batch/BendersByBatch.cpp index e08d1c65b..1f5653ed7 100644 --- a/src/cpp/benders/benders_by_batch/BendersByBatch.cpp +++ b/src/cpp/benders/benders_by_batch/BendersByBatch.cpp @@ -46,6 +46,7 @@ void BendersByBatch::InitializeProblems() { problem_count++; } } + init_problems_ = false; } void BendersByBatch::BroadcastSingleSubpbCostsUnderApprox() { DblVector single_subpb_costs_under_approx(_data.nsubproblem); @@ -57,7 +58,13 @@ void BendersByBatch::BroadcastSingleSubpbCostsUnderApprox() { SetAlpha_i(single_subpb_costs_under_approx); } void BendersByBatch::Run() { - PreRunInitialization(); + if (init_data_) { + PreRunInitialization(); + } else { + // only ? + _data.stop = false; + } + MasterLoop(); if (Rank() == rank_0) { compute_ub(); @@ -94,11 +101,7 @@ void BendersByBatch::MasterLoop() { remaining_epsilon_ = Gap(); if (Rank() == rank_0) { - _logger->display_message( - " _______________________________________________________________" - "_" - "________"); - _logger->display_message("/"); + _logger->PrintIterationSeparatorBegin(); _logger->display_message("\tSolving master..."); get_master_value(); @@ -127,9 +130,7 @@ void BendersByBatch::MasterLoop() { _logger->LogSubproblemsSolvingCumulativeCpuTime( GetSubproblemsCumulativeCpuTime()); _logger->LogSubproblemsSolvingWalltime(GetSubproblemsWalltime()); - _logger->display_message( - "\\________________________________________________________________" - "________"); + _logger->PrintIterationSeparatorEnd(); mathLoggerDriver_->Print(_data); } } @@ -234,8 +235,9 @@ void BendersByBatch::BuildCut( misprice_ = global_misprice; Gather(subproblem_data_map, gathered_subproblem_map, rank_0); SetSubproblemsWalltime(subproblems_timer_per_proc.elapsed()); + for (const auto &subproblem_map : gathered_subproblem_map) { - for (auto &&[_, subproblem_data] : subproblem_map) { + for (auto &&[sub_problem_name, subproblem_data] : subproblem_map) { SetSubproblemCost(GetSubproblemCost() + subproblem_data.subproblem_cost); BoundSimplexIterations(subproblem_data.simplex_iter); } @@ -264,7 +266,7 @@ void BendersByBatch::GetSubproblemCut( if (std::find(batch_sub_problems.cbegin(), batch_sub_problems.cend(), name) != batch_sub_problems.cend()) { Timer subproblem_timer; - SubProblemData subproblem_data; + PlainData::SubProblemData subproblem_data; worker->fix_to(_data.x_cut); worker->solve(subproblem_data.lpstatus, Options().OUTPUTROOT, Options().LAST_MASTER_MPS + MPS_SUFFIX, _writer); diff --git a/src/cpp/benders/benders_core/BendersBase.cpp b/src/cpp/benders/benders_core/BendersBase.cpp index 9fa96397d..b0c94599c 100644 --- a/src/cpp/benders/benders_core/BendersBase.cpp +++ b/src/cpp/benders/benders_core/BendersBase.cpp @@ -26,17 +26,22 @@ BendersBase::BendersBase(BendersBaseOptions options, Logger logger, * \brief Initialize set of data used in the loop */ void BendersBase::init_data() { - _data.lb = -1e20; - _data.ub = +1e20; - _data.best_ub = +1e20; + _data.lb = relevantIterationData_.last._lb = -1e20; + _data.ub = relevantIterationData_.last._ub = +1e20; + _data.best_ub = relevantIterationData_.last._best_ub = +1e20; _data.stop = false; _data.it = 0; _data.overall_subpb_cost_under_approx = 0; - _data.invest_cost = 0; + _data.invest_cost = relevantIterationData_.last._invest_cost = 0; _data.best_it = 0; _data.stopping_criterion = StoppingCriterion::empty; _data.is_in_initial_relaxation = false; _data.cumulative_number_of_subproblem_solved = 0; + relevantIterationData_.best = relevantIterationData_.last; + _data.benders_time = 0; + _data.iteration_time = 0; + _data.timer_master = 0; + _data.subproblems_walltime = 0; } void BendersBase::OpenCsvFile() { @@ -61,17 +66,17 @@ void BendersBase::CloseCsvFile() { } } void BendersBase::PrintCurrentIterationCsv() { - if (relevantIterationData_.last->_valid) { + if (relevantIterationData_.last._valid) { auto ite = _data.it - 1; Point x_cut; // Write first problem : use result of best iteration if (ite == 0) { int best_it_index = _data.best_it - 1; - if (best_it_index >= 0 && relevantIterationData_.best != nullptr) { - x_cut = relevantIterationData_.best->get_x_cut(); + if (best_it_index >= 0) { + x_cut = relevantIterationData_.best.get_x_cut(); } } else { - x_cut = relevantIterationData_.last->get_x_cut(); + x_cut = relevantIterationData_.last.get_x_cut(); } print_master_and_cut(_csv_file, ite + 1 + iterations_before_resume, relevantIterationData_.last, x_cut); @@ -89,7 +94,8 @@ void BendersBase::PrintCurrentIterationCsv() { * * \param subproblem_index : problem id */ -void print_cut_csv(std::ostream &stream, const SubProblemData &subproblem_data, +void print_cut_csv(std::ostream &stream, + const PlainData::SubProblemData &subproblem_data, std::string const &subproblem_name, int subproblem_index, double alpha_i) { stream << "Subproblem" @@ -108,13 +114,13 @@ void print_cut_csv(std::ostream &stream, const SubProblemData &subproblem_data, } void BendersBase::print_master_and_cut(std::ostream &file, int ite, - WorkerMasterDataPtr &trace, + WorkerMasterData &trace, Point const &x_cut) { file << ite << ";"; print_master_csv(file, trace, x_cut); - for (auto &[subproblem_name, subproblem_data] : trace->_cut_trace) { + for (auto &[subproblem_name, subproblem_data] : trace._cut_trace) { auto problem_id = _problem_to_id[subproblem_name]; file << ite << ";"; print_cut_csv(file, subproblem_data, subproblem_name, problem_id, @@ -132,19 +138,19 @@ void BendersBase::print_master_and_cut(std::ostream &file, int ite, * \param x_cut : cut point determined after the master resolution */ void BendersBase::print_master_csv(std::ostream &stream, - const WorkerMasterDataPtr &trace, + const WorkerMasterData &trace, Point const &x_cut) const { stream << "Master" << ";"; stream << _options.MASTER_NAME << ";"; stream << _data.nsubproblem << ";"; - stream << trace->_ub << ";"; - stream << trace->_lb << ";"; - stream << trace->_best_ub << ";"; + stream << trace._ub << ";"; + stream << trace._lb << ";"; + stream << trace._best_ub << ";"; stream << ";"; - stream << norm_point(x_cut, trace->get_x_cut()) << ";"; + stream << norm_point(x_cut, trace.get_x_cut()) << ";"; stream << ";"; - stream << trace->_master_duration << ";"; + stream << trace._master_duration << ";"; stream << std::endl; } @@ -159,7 +165,8 @@ void BendersBase::update_best_ub() { _data.x_in = _data.x_cut; _data.best_ub = _data.ub; _data.best_it = _data.it; - relevantIterationData_.best = relevantIterationData_.last; + FillWorkerMasterData(relevantIterationData_.best); + relevantIterationData_.best._cut_trace = relevantIterationData_.last._cut_trace; best_iteration_data = bendersDataToLogData(_data); } } @@ -197,6 +204,21 @@ bool BendersBase::ShouldBendersStop() { return (_data.stopping_criterion != StoppingCriterion::empty) && !_data.is_in_initial_relaxation; } +void BendersBase::FillWorkerMasterData(WorkerMasterData &worker_master_data) { + worker_master_data._lb = _data.lb; + worker_master_data._ub = _data.ub; + worker_master_data._best_ub = _data.best_ub; + worker_master_data._x_in = std::make_shared(_data.x_in); + worker_master_data._x_out = std::make_shared(_data.x_out); + worker_master_data._x_cut = std::make_shared(_data.x_cut); + worker_master_data._max_invest = std::make_shared(_data.max_invest); + worker_master_data._min_invest = std::make_shared(_data.min_invest); + worker_master_data._master_duration = _data.timer_master; + worker_master_data._subproblem_duration = _data.subproblems_walltime; + worker_master_data._invest_cost = _data.invest_cost; + worker_master_data._operational_cost = _data.subproblem_cost; + worker_master_data._valid = true; +} /*! * \brief Update trace of the Benders for the current iteration @@ -204,22 +226,9 @@ bool BendersBase::ShouldBendersStop() { * Fonction to store the current Benders data in the trace */ void BendersBase::UpdateTrace() { - auto &LastWorkerMasterDataPtr = relevantIterationData_.last; - LastWorkerMasterDataPtr->_lb = _data.lb; - LastWorkerMasterDataPtr->_ub = _data.ub; - LastWorkerMasterDataPtr->_best_ub = _data.best_ub; - LastWorkerMasterDataPtr->_x_in = std::make_shared(_data.x_in); - LastWorkerMasterDataPtr->_x_out = std::make_shared(_data.x_out); - LastWorkerMasterDataPtr->_x_cut = std::make_shared(_data.x_cut); - LastWorkerMasterDataPtr->_max_invest = - std::make_shared(_data.max_invest); - LastWorkerMasterDataPtr->_min_invest = - std::make_shared(_data.min_invest); - LastWorkerMasterDataPtr->_master_duration = _data.timer_master; - LastWorkerMasterDataPtr->_subproblem_duration = _data.subproblems_walltime; - LastWorkerMasterDataPtr->_invest_cost = _data.invest_cost; - LastWorkerMasterDataPtr->_operational_cost = _data.subproblem_cost; - LastWorkerMasterDataPtr->_valid = true; + FillWorkerMasterData(relevantIterationData_.last); + // TODO Outer loop --> de-comment for general case + // workerMasterDataVect_.push_back(relevantIterationData_.last); } bool BendersBase::is_initial_relaxation_requested() const { @@ -327,8 +336,7 @@ void BendersBase::ComputeInvestCost() { _data.invest_cost = 0; int ncols = _master->_solver->get_ncols(); - std::vector obj(ncols); - _master->_solver->get_obj(obj.data(), 0, ncols - 1); + std::vector obj(MasterObjectiveFunctionCoeffs()); for (const auto &[col_name, value] : _data.x_cut) { int col_id = _master->_name_to_id[col_name]; @@ -366,11 +374,12 @@ void BendersBase::GetSubproblemCut(SubProblemDataMap &subproblem_data_map) { const std::pair &kvp) { const auto &[name, worker] = kvp; Timer subproblem_timer; - SubProblemData subproblem_data; + PlainData::SubProblemData subproblem_data; worker->fix_to(_data.x_cut); worker->solve(subproblem_data.lpstatus, _options.OUTPUTROOT, _options.LAST_MASTER_MPS + MPS_SUFFIX, _writer); worker->get_value(subproblem_data.subproblem_cost); + worker->get_solution(subproblem_data.variables); worker->get_subgradient(subproblem_data.var_name_and_subgradient); worker->get_splex_num_of_ite_last(subproblem_data.simplex_iter); subproblem_data.subproblem_timer = subproblem_timer.elapsed(); @@ -398,7 +407,7 @@ void BendersBase::compute_cut(const SubProblemDataMap &subproblem_data_map) { _master->addSubproblemCut(_problem_to_id[subproblem_name], subproblem_data.var_name_and_subgradient, _data.x_cut, subproblem_data.subproblem_cost); - relevantIterationData_.last->_cut_trace[subproblem_name] = subproblem_data; + relevantIterationData_.last._cut_trace[subproblem_name] = subproblem_data; } } @@ -432,7 +441,7 @@ void BendersBase::compute_cut_aggregate( compute_cut_val(subproblem_data.var_name_and_subgradient, _data.x_cut, s); - relevantIterationData_.last->_cut_trace[name] = subproblem_data; + relevantIterationData_.last._cut_trace[name] = subproblem_data; } _master->add_cut(s, _data.x_cut, rhs); } @@ -483,9 +492,9 @@ void BendersBase::post_run_actions() const { } void BendersBase::SaveCurrentIterationInOutputFile() const { - auto &LastWorkerMasterDataPtr = relevantIterationData_.last; - if (LastWorkerMasterDataPtr->_valid) { - _writer->write_iteration(iteration(LastWorkerMasterDataPtr), + auto &LastWorkerMasterData = relevantIterationData_.last; + if (LastWorkerMasterData._valid) { + _writer->write_iteration(iteration(LastWorkerMasterData), _data.it + iterations_before_resume); _writer->dump(); } @@ -495,15 +504,14 @@ void BendersBase::SaveSolutionInOutputFile() const { _writer->dump(); } -Output::CandidatesVec candidates_data( - const WorkerMasterDataPtr &masterDataPtr_l) { +Output::CandidatesVec candidates_data(const WorkerMasterData &masterDataPtr_l) { Output::CandidatesVec candidates_vec; - for (const auto &[cand_name, cand_value] : masterDataPtr_l->get_x_cut()) { + for (const auto &[cand_name, cand_value] : masterDataPtr_l.get_x_cut()) { Output::CandidateData candidate_data; candidate_data.name = cand_name; candidate_data.invest = cand_value; - candidate_data.min = masterDataPtr_l->get_min_invest()[cand_name]; - candidate_data.max = masterDataPtr_l->get_max_invest()[cand_name]; + candidate_data.min = masterDataPtr_l.get_min_invest()[cand_name]; + candidate_data.max = masterDataPtr_l.get_max_invest()[cand_name]; candidates_vec.push_back(candidate_data); } @@ -511,20 +519,20 @@ Output::CandidatesVec candidates_data( } Output::Iteration BendersBase::iteration( - const WorkerMasterDataPtr &masterDataPtr_l) const { + const WorkerMasterData &masterDataPtr_l) const { Output::Iteration iteration; - iteration.master_duration = masterDataPtr_l->_master_duration; - iteration.subproblem_duration = masterDataPtr_l->_subproblem_duration; - iteration.lb = masterDataPtr_l->_lb; - iteration.ub = masterDataPtr_l->_ub; - iteration.best_ub = masterDataPtr_l->_best_ub; - iteration.optimality_gap = masterDataPtr_l->_best_ub - masterDataPtr_l->_lb; - iteration.relative_gap = (masterDataPtr_l->_best_ub - masterDataPtr_l->_lb) / - masterDataPtr_l->_best_ub; - iteration.investment_cost = masterDataPtr_l->_invest_cost; - iteration.operational_cost = masterDataPtr_l->_operational_cost; + iteration.master_duration = masterDataPtr_l._master_duration; + iteration.subproblem_duration = masterDataPtr_l._subproblem_duration; + iteration.lb = masterDataPtr_l._lb; + iteration.ub = masterDataPtr_l._ub; + iteration.best_ub = masterDataPtr_l._best_ub; + iteration.optimality_gap = masterDataPtr_l._best_ub - masterDataPtr_l._lb; + iteration.relative_gap = (masterDataPtr_l._best_ub - masterDataPtr_l._lb) / + masterDataPtr_l._best_ub; + iteration.investment_cost = masterDataPtr_l._invest_cost; + iteration.operational_cost = masterDataPtr_l._operational_cost; iteration.overall_cost = - masterDataPtr_l->_invest_cost + masterDataPtr_l->_operational_cost; + masterDataPtr_l._invest_cost + masterDataPtr_l._operational_cost; iteration.candidates = candidates_data(masterDataPtr_l); iteration.cumulative_number_of_subproblem_resolved = _data.cumulative_number_of_subproblem_solved + @@ -567,11 +575,9 @@ Output::SolutionData BendersBase::solution() const { } else { const auto &best_iteration_worker_master_data = relevantIterationData_.best; - if (best_iteration_worker_master_data != nullptr) { - solution_data.solution = iteration(best_iteration_worker_master_data); - solution_data.solution.optimality_gap = optimal_gap; - solution_data.solution.relative_gap = relative_gap; - } + solution_data.solution = iteration(best_iteration_worker_master_data); + solution_data.solution.optimality_gap = optimal_gap; + solution_data.solution.relative_gap = relative_gap; } solution_data.stopping_criterion = criterion_to_str(_data.stopping_criterion); return solution_data; @@ -624,6 +630,13 @@ std::filesystem::path BendersBase::get_master_path() const { return std::filesystem::path(_options.INPUTROOT) / (_options.MASTER_NAME + MPS_SUFFIX); } +/*! + * \brief Get path to last mps file of master problem + */ +std::filesystem::path BendersBase::LastMasterPath() const { + return std::filesystem::path(_options.OUTPUTROOT) / + (_options.LAST_MASTER_MPS + MPS_SUFFIX); +} /*! * \brief Get path to structure txt file from options @@ -688,8 +701,12 @@ std::map BendersBase::get_master_variable_map( void BendersBase::reset_master(WorkerMaster *worker_master) { _master.reset(worker_master); + master_is_empty_ = false; +} +void BendersBase::free_master() { + _master->free(); + master_is_empty_ = true; } -void BendersBase::free_master() const { _master->free(); } WorkerMasterPtr BendersBase::get_master() const { return _master; } void BendersBase::AddSubproblem( @@ -826,8 +843,8 @@ void BendersBase::SaveCurrentBendersData() { PrintCurrentIterationCsv(); } } -void BendersBase::ClearCurrentIterationCutTrace() const { - relevantIterationData_.last->_cut_trace.clear(); +void BendersBase::ClearCurrentIterationCutTrace() { + relevantIterationData_.last._cut_trace.clear(); } void BendersBase::EndWritingInOutputFile() const { _writer->updateEndTime(); @@ -840,3 +857,90 @@ void BendersBase::write_basis() const { (_options.LAST_MASTER_BASIS)); _master->write_basis(filename); } + +WorkerMasterDataVect BendersBase::AllCuts() const { + return workerMasterDataVect_; +} + +int BendersBase::MasterRowIndex(const std::string &row_name) const { + return _master->RowIndex(row_name); +} + +void BendersBase::MasterChangeRhs(int id_row, double val) const { + _master->ChangeRhs(id_row, val); +} + +void BendersBase::MasterGetRhs(double &rhs, int id_row) const { + _master->GetRhs(&rhs, id_row); +} + +void BendersBase::MasterAddRows( + std::vector const &qrtype_p, std::vector const &rhs_p, + std::vector const &range_p, std::vector const &mstart_p, + std::vector const &mclind_p, std::vector const &dmatval_p, + const std::vector &row_names) const { + _master->AddRows(qrtype_p, rhs_p, range_p, mstart_p, mclind_p, dmatval_p, + row_names); +} +void BendersBase::ResetMasterFromLastIteration() { + reset_master(new WorkerMaster(master_variable_map_, LastMasterPath(), + get_solver_name(), get_log_level(), + _data.nsubproblem, solver_log_manager_, + IsResumeMode(), _logger)); +} +bool BendersBase::MasterIsEmpty() const { return master_is_empty_; } + +std::vector BendersBase::MasterObjectiveFunctionCoeffs() const { + int ncols = _master->_solver->get_ncols(); + std::vector obj(ncols); + _master->_solver->get_obj(obj.data(), 0, ncols - 1); + return obj; +} + +void BendersBase::MasterRowsCoeffs(std::vector &mstart, + std::vector &mclind, + std::vector &dmatval, int size, + std::vector &nels, int first, + int last) const { + _master->_solver->get_rows(mstart.data(), mclind.data(), dmatval.data(), size, + nels.data(), first, last); +} +int BendersBase::MasterGetNElems() const { + return _master->_solver->get_nelems(); +} +void BendersBase::SetMasterObjectiveFunctionCoeffsToZeros() const { + // assuming that master var id are in [0, size-1] + auto master_vars_size = master_variable_map_.size(); + std::vector zeros(master_vars_size, 0.0); + SetMasterObjectiveFunction(zeros.data(), 0, master_vars_size - 1); +} + +void BendersBase::SetMasterObjectiveFunction(const double *coeffs, int first, + int last) const { + _master->_solver->set_obj(coeffs, first, last); +} + +int BendersBase::MasterGetnrows() const { return _master->Getnrows(); } +int BendersBase::MasterGetncols() const { return _master->Getncols(); } +void BendersBase::MasterGetRowType(std::vector &qrtype, int first, + int last) const { + _master->_solver->get_row_type(qrtype.data(), first, last); +} + +WorkerMasterData BendersBase::BestIterationWorkerMaster() const { + return relevantIterationData_.best; +} + +void BendersBase::ResetData(double criterion) { + init_data(); + _data.external_loop_criterion = criterion; +} + +void BendersBase::InitExternalValues() { + _data.external_loop_criterion = 0; + _data.benders_num_run = 0; +} + +CurrentIterationData BendersBase::GetCurrentIterationData() const { + return _data; +} diff --git a/src/cpp/benders/benders_core/BendersMathLogger.cpp b/src/cpp/benders/benders_core/BendersMathLogger.cpp index 4c75de659..ccf2ace3d 100644 --- a/src/cpp/benders/benders_core/BendersMathLogger.cpp +++ b/src/cpp/benders/benders_core/BendersMathLogger.cpp @@ -1,12 +1,20 @@ #include "BendersMathLogger.h" +#include +#include + #include "LogUtils.h" #include "LoggerUtils.h" -HeadersManager::HeadersManager(HEADERSTYPE type, const BENDERSMETHOD& method) { +HeadersManager::HeadersManager(HEADERSTYPE type, const BENDERSMETHOD& method) + : type_(type), method_(method) {} + +std::vector HeadersManager::HeadersList() { + std::vector headers_list; + headers_list.push_back("Ite"); headers_list.push_back("Lb"); - if (method == BENDERSMETHOD::BENDERS) { + if (method_ == BENDERSMETHOD::BENDERS) { headers_list.push_back("Ub"); headers_list.push_back("BestUb"); headers_list.push_back("AbsGap"); @@ -15,20 +23,38 @@ HeadersManager::HeadersManager(HEADERSTYPE type, const BENDERSMETHOD& method) { headers_list.push_back("MinSpx"); headers_list.push_back("MaxSpx"); - if (type == HEADERSTYPE::LONG || method == BENDERSMETHOD::BENDERSBYBATCH) { + if (type_ == HEADERSTYPE::LONG || + method_ == BENDERSMETHOD::BENDERS_BY_BATCH) { headers_list.push_back("NbSubPbSolv"); } - if (type == HEADERSTYPE::LONG) { + if (type_ == HEADERSTYPE::LONG) { headers_list.push_back("CumulNbSubPbSolv"); } headers_list.push_back("IteTime (s)"); headers_list.push_back("MasterTime (s)"); headers_list.push_back("SPWallTime (s)"); - if (type == HEADERSTYPE::LONG) { + + if (type_ == HEADERSTYPE::LONG) { headers_list.push_back("SPCpuTime (s)"); headers_list.push_back("NotSolvingWallTime (s)"); } + + return headers_list; +} + +HeadersManagerExternalLoop::HeadersManagerExternalLoop( + HEADERSTYPE type, const BENDERSMETHOD& method) + : HeadersManager(type, method) {} + +std::vector HeadersManagerExternalLoop::HeadersList() { + std::vector headers_list; + headers_list.push_back("Outer loop"); + headers_list.push_back("Criterion value"); + auto base_headers = HeadersManager::HeadersList(); + std::move(base_headers.begin(), base_headers.end(), + std::back_inserter(headers_list)); + return headers_list; } LogDestination::LogDestination(std::streamsize width) @@ -51,6 +77,119 @@ LogDestination::LogDestination(const std::filesystem::path& file_path, std::cerr << err_msg.str(); } } + +void MathLoggerBehaviour::PrintIterationSeparatorBegin() { + std::string sep_msg("/*\\"); + sep_msg += std::string(74, '-'); + LogsDestination() << sep_msg << std::endl; +} + +void MathLoggerBehaviour::PrintIterationSeparatorEnd() { + std::string sep_msg(74, '-'); + sep_msg = "\\*/" + sep_msg; + LogsDestination() << sep_msg << std::endl; +} + +void MathLoggerBase::Print(const CurrentIterationData& data) { + PrintBendersData(LogsDestination(), data, HeadersType(), + BENDERSMETHOD::BENDERS); +} +void MathLoggerBase::setHeadersList() { + auto type = HeadersType(); + HeadersManager headers_manager(type, BENDERSMETHOD::BENDERS); + MathLogger::setHeadersList(headers_manager.HeadersList()); +} + +void MathLoggerBaseExternalLoop::setHeadersList() { + auto type = HeadersType(); + HeadersManagerExternalLoop headers_manager(type, BENDERSMETHOD::BENDERS); + MathLogger::setHeadersList(headers_manager.HeadersList()); +} + +void MathLogger::setHeadersList(const std::vector& headers) { + headers_.clear(); + headers_ = headers; +} + +double getDurationNotSolving(double iteration, double master, + double subproblems) { + return iteration - master - subproblems; +} + +void PrintBendersData(LogDestination& log_destination, + const CurrentIterationData& data, const HEADERSTYPE& type, + const BENDERSMETHOD& method) { + log_destination << data.it; + log_destination << std::scientific << std::setprecision(10) << data.lb; + if (method == BENDERSMETHOD::BENDERS) { + log_destination << std::scientific << std::setprecision(10) << data.ub; + log_destination << std::scientific << std::setprecision(10) << data.best_ub; + log_destination << std::scientific << std::setprecision(2) + << data.best_ub - data.lb; + log_destination << std::scientific << std::setprecision(2) + << (data.best_ub - data.lb) / data.best_ub; + } + log_destination << data.min_simplexiter; + log_destination << data.max_simplexiter; + if (type == HEADERSTYPE::LONG || method == BENDERSMETHOD::BENDERS_BY_BATCH) { + log_destination << data.number_of_subproblem_solved; + } + if (type == HEADERSTYPE::LONG) { + log_destination << data.cumulative_number_of_subproblem_solved; + } + + log_destination << std::setprecision(2) << data.iteration_time; + log_destination << std::setprecision(2) << data.timer_master; + log_destination << std::setprecision(2) << data.subproblems_walltime; + + if (type == HEADERSTYPE::LONG) { + log_destination << std::setprecision(2) + << data.subproblems_cumulative_cputime; + log_destination << std::setprecision(2) + << getDurationNotSolving(data.iteration_time, + data.timer_master, + data.subproblems_walltime); + } + log_destination << std::endl; +} + +void PrintExternalLoopData(LogDestination& log_destination, + const CurrentIterationData& data, + const HEADERSTYPE& type, + const BENDERSMETHOD& method) { + log_destination << data.benders_num_run; + log_destination << std::scientific << std::setprecision(10) + << data.external_loop_criterion; + PrintBendersData(log_destination, data, type, method); +} +void MathLoggerBaseExternalLoop::Print(const CurrentIterationData& data) { + PrintExternalLoopData(LogsDestination(), data, HeadersType(), + BENDERSMETHOD::BENDERS); +} + +void MathLoggerBendersByBatch::setHeadersList() { + auto type = HeadersType(); + HeadersManager headers_manager(type, BENDERSMETHOD::BENDERS_BY_BATCH); + + MathLogger::setHeadersList(headers_manager.HeadersList()); +} + +void MathLoggerBendersByBatchExternalLoop::setHeadersList() { + auto type = HeadersType(); + HeadersManagerExternalLoop headers_manager(type, + BENDERSMETHOD::BENDERS_BY_BATCH); + MathLogger::setHeadersList(headers_manager.HeadersList()); +} + +void MathLoggerBendersByBatch::Print(const CurrentIterationData& data) { + PrintBendersData(LogsDestination(), data, HeadersType(), + BENDERSMETHOD::BENDERS_BY_BATCH); +} +void MathLoggerBendersByBatchExternalLoop::Print( + const CurrentIterationData& data) { + PrintExternalLoopData(LogsDestination(), data, HeadersType(), + BENDERSMETHOD::BENDERS); +} void MathLoggerDriver::add_logger( std::shared_ptr logger) { if (logger) { @@ -75,3 +214,65 @@ void MathLoggerDriver::display_message(const std::string& str) { logger->display_message(str); } } + +void MathLoggerDriver::PrintIterationSeparatorBegin() { + for (auto logger : math_loggers_) { + logger->PrintIterationSeparatorBegin(); + } +} + +void MathLoggerDriver::PrintIterationSeparatorEnd() { + for (auto logger : math_loggers_) { + logger->PrintIterationSeparatorEnd(); + } +} + +MathLoggerImplementation::MathLoggerImplementation( + const BENDERSMETHOD& method, const std::filesystem::path& file_path, + std::streamsize width, HEADERSTYPE type) { + switch (method) { + case BENDERSMETHOD::BENDERS: + implementation_ = + std::make_shared(file_path, width, type); + break; + case BENDERSMETHOD::BENDERS_EXTERNAL_LOOP: + implementation_ = + std::make_shared(file_path, width, type); + break; + case BENDERSMETHOD::BENDERS_BY_BATCH: + implementation_ = + std::make_shared(file_path, width, type); + break; + case BENDERSMETHOD::BENDERS_BY_BATCH_EXTERNAL_LOOP: + implementation_ = std::make_shared( + file_path, width, type); + break; + + default: + break; + } +} + +MathLoggerImplementation::MathLoggerImplementation(const BENDERSMETHOD& method, + std::streamsize width, + HEADERSTYPE type) { + switch (method) { + case BENDERSMETHOD::BENDERS: + implementation_ = std::make_shared(width, type); + break; + case BENDERSMETHOD::BENDERS_EXTERNAL_LOOP: + implementation_ = + std::make_shared(width, type); + break; + case BENDERSMETHOD::BENDERS_BY_BATCH: + implementation_ = std::make_shared(width, type); + break; + case BENDERSMETHOD::BENDERS_BY_BATCH_EXTERNAL_LOOP: + implementation_ = + std::make_shared(width, type); + break; + + default: + break; + } +} diff --git a/src/cpp/benders/benders_core/SimulationOptions.cpp b/src/cpp/benders/benders_core/SimulationOptions.cpp index d88d0cc9a..13cbe07b5 100644 --- a/src/cpp/benders/benders_core/SimulationOptions.cpp +++ b/src/cpp/benders/benders_core/SimulationOptions.cpp @@ -11,7 +11,8 @@ Json::Value get_value_from_json(const std::filesystem::path &file_name) { Json::CharReaderBuilder builder_l; std::string errs; if (!parseFromStream(builder_l, input_file_l, &_input, &errs)) { - std::cerr << LOGLOCATION << "Invalid options file: " << file_name; + std::cerr << LOGLOCATION << "Invalid options file: " << file_name << "\n" + << errs; std::exit(1); } return _input; @@ -169,4 +170,9 @@ BendersBaseOptions SimulationOptions::get_benders_options() const { result.BATCH_SIZE = BATCH_SIZE; return result; +} + +ExternalLoopOptions SimulationOptions::GetExternalLoopOptions() const { + return {EXT_LOOP_CRITERION_VALUE, EXT_LOOP_CRITERION_TOLERANCE, + EXT_LOOP_CRITERION_COUNT_THRESHOLD}; } \ No newline at end of file diff --git a/src/cpp/benders/benders_core/SubproblemWorker.cpp b/src/cpp/benders/benders_core/SubproblemWorker.cpp index 111ed586b..e88cea2a5 100644 --- a/src/cpp/benders/benders_core/SubproblemWorker.cpp +++ b/src/cpp/benders/benders_core/SubproblemWorker.cpp @@ -67,3 +67,20 @@ void SubproblemWorker::get_subgradient(Point &s) const { s[kvp.second] = +ptr[kvp.first]; } } + +/*! + * \brief Return the solutions values of a problem + * + * \param lb : reference to a map + */ +void SubproblemWorker::get_solution(PlainData::Variables &vars) const { + vars.values = std::vector(_solver->get_ncols()); + + if (_solver->get_n_integer_vars() > 0) { + _solver->get_mip_sol(vars.values.data()); + } else { + _solver->get_lp_sol(vars.values.data(), NULL, NULL); + } + + vars.names = _solver->get_col_names(); +} \ No newline at end of file diff --git a/src/cpp/benders/benders_core/Worker.cpp b/src/cpp/benders/benders_core/Worker.cpp index 8eb22cb49..2859df599 100644 --- a/src/cpp/benders/benders_core/Worker.cpp +++ b/src/cpp/benders/benders_core/Worker.cpp @@ -116,4 +116,30 @@ void Worker::get_splex_num_of_ite_last(int &result) const { void Worker::write_basis(const std::filesystem::path &filename) const { _solver->write_basis(filename); -} \ No newline at end of file +} + +int Worker::RowIndex(const std::string &row_name) const { + return _solver->get_row_index(row_name); +} + +void Worker::ChangeRhs(int id_row, double val) const { + _solver->chg_rhs(id_row, val); +} + +void Worker::GetRhs(double *val, int id_row) const { + _solver->get_rhs(val, id_row, id_row); +} + +void Worker::AddRows(std::vector const &qrtype_p, + std::vector const &rhs_p, + std::vector const &range_p, + std::vector const &mstart_p, + std::vector const &mclind_p, + std::vector const &dmatval_p, + const std::vector &row_names) const { + solver_addrows(*_solver, qrtype_p, rhs_p, {}, mstart_p, mclind_p, dmatval_p, + row_names); +} + +int Worker::Getnrows() const { return _solver->get_nrows(); } +int Worker::Getncols() const { return _solver->get_ncols(); } diff --git a/src/cpp/benders/benders_core/include/BendersBase.h b/src/cpp/benders/benders_core/include/BendersBase.h index 4961ba764..3054d3712 100644 --- a/src/cpp/benders/benders_core/include/BendersBase.h +++ b/src/cpp/benders/benders_core/include/BendersBase.h @@ -39,18 +39,66 @@ class BendersBase { } double execution_time() const; virtual std::string BendersName() const = 0; + // TODO rename to be consistent with data that it hold + // ref of value? + WorkerMasterDataVect AllCuts() const; + // BendersCuts CutsBestIteration() const; + // void Clean(); + LogData GetBestIterationData() const; void set_input_map(const CouplingMap &coupling_map); + int MasterRowIndex(const std::string &row_name) const; + void MasterChangeRhs(int id_row, double val) const; + // for test + void MasterGetRhs(double &rhs, int id_row) const; + const VariableMap &MasterVariables() const { return master_variable_map_; } + std::vector MasterObjectiveFunctionCoeffs() const; + void MasterRowsCoeffs(std::vector &mstart, std::vector &mclind, + std::vector &dmatval, int size, + std::vector &nels, int first, int last) const; + int MasterGetNElems() const; + void MasterAddRows(std::vector const &qrtype_p, + std::vector const &rhs_p, + std::vector const &range_p, + std::vector const &mstart_p, + std::vector const &mclind_p, + std::vector const &dmatval_p, + const std::vector &row_names = {}) const; + void MasterGetRowType(std::vector &qrtype, int first, int last) const; + void ResetMasterFromLastIteration(); + std::filesystem::path LastMasterPath() const; + bool MasterIsEmpty() const; + void DoFreeProblems(bool free_problems) { free_problems_ = free_problems; } + int MasterGetnrows() const; + int MasterGetncols() const; + WorkerMasterData BestIterationWorkerMaster() const; + void SetMasterObjectiveFunctionCoeffsToZeros() const; + void SetMasterObjectiveFunction(const double *coeffs, int first, + int last) const; + virtual void InitializeProblems() = 0; + void SetMaxIteration(int max_iteration) { + _options.MAX_ITERATIONS = max_iteration; + } + BendersBaseOptions Options() const { return _options; } + void ResetData(double criterion); + virtual void free() = 0; + void InitExternalValues(); + int GetBendersRunNumber() const { return _data.benders_num_run; } + CurrentIterationData GetCurrentIterationData() const; protected: CurrentIterationData _data; + WorkerMasterDataVect workerMasterDataVect_; + // BendersCuts best_iteration_cuts_; + // BendersCuts current_iteration_cuts_; VariableMap master_variable_map_; CouplingMap coupling_map_; - std::shared_ptr mathLoggerDriver_; + // for warmstart initialize all data, master, subproblem etc... + bool init_data_ = true; + bool init_problems_ = true; + bool free_problems_ = true; protected: - virtual void free() = 0; virtual void Run() = 0; - virtual void InitializeProblems() = 0; virtual void init_data(); void update_best_ub(); bool ShouldBendersStop(); @@ -77,7 +125,7 @@ class BendersBase { [[nodiscard]] LogData bendersDataToLogData( const CurrentIterationData &data) const; virtual void reset_master(WorkerMaster *worker_master); - void free_master() const; + void free_master(); void free_subproblems(); void AddSubproblem(const std::pair &kvp); [[nodiscard]] WorkerMasterPtr get_master() const; @@ -106,7 +154,6 @@ class BendersBase { return std::filesystem::path(_options.LAST_ITERATION_JSON_FILE); } void UpdateMaxNumberIterationResumeMode(const unsigned nb_iteration_done); - LogData GetBestIterationData() const; void SaveCurrentIterationInOutputFile() const; void SaveSolutionInOutputFile() const; void PrintCurrentIterationCsv(); @@ -114,7 +161,7 @@ class BendersBase { void CloseCsvFile(); void ChecksResumeMode(); virtual void SaveCurrentBendersData(); - void ClearCurrentIterationCutTrace() const; + void ClearCurrentIterationCutTrace(); virtual void EndWritingInOutputFile() const; [[nodiscard]] int GetNumIterationsBeforeRestart() const { return iterations_before_resume; @@ -134,7 +181,6 @@ class BendersBase { int ProblemToId(const std::string &problem_name) const { return _problem_to_id.at(problem_name); } - BendersBaseOptions Options() const { return _options; } virtual void UpdateStoppingCriterion(); virtual bool ShouldRelaxationStop() const; int GetNumOfSubProblemsSolvedBeforeResume() { @@ -148,8 +194,8 @@ class BendersBase { private: void print_master_and_cut(std::ostream &file, int ite, - WorkerMasterDataPtr &trace, Point const &xopt); - void print_master_csv(std::ostream &stream, const WorkerMasterDataPtr &trace, + WorkerMasterData &trace, Point const &xopt); + void print_master_csv(std::ostream &stream, const WorkerMasterData &trace, Point const &xopt) const; void check_status(const SubProblemDataMap &subproblem_data_map) const; [[nodiscard]] LogData build_log_data_from_data() const; @@ -160,15 +206,17 @@ class BendersBase { [[nodiscard]] std::map get_master_variable_map( const std::map> &input_map) const; [[nodiscard]] virtual bool shouldParallelize() const = 0; - Output::Iteration iteration(const WorkerMasterDataPtr &masterDataPtr_l) const; + Output::Iteration iteration(const WorkerMasterData &masterDataPtr_l) const; LogData FinalLogData() const; + void FillWorkerMasterData(WorkerMasterData &workerMasterData); private: + bool master_is_empty_ = true; BendersBaseOptions _options; unsigned int _totalNbProblems = 0; std::filesystem::path solver_log_file_ = ""; - BendersRelevantIterationsData relevantIterationData_ = { - std::make_shared(), nullptr}; + BendersRelevantIterationsData relevantIterationData_ = {WorkerMasterData(), + WorkerMasterData()}; WorkerMasterPtr _master; VariableMap _problem_to_id; SubproblemsMapPtr subproblem_map; @@ -183,5 +231,6 @@ class BendersBase { public: Logger _logger; Writer _writer; + std::shared_ptr mathLoggerDriver_; }; using pBendersBase = std::shared_ptr; diff --git a/src/cpp/benders/benders_core/include/BendersMathLogger.h b/src/cpp/benders/benders_core/include/BendersMathLogger.h index 346573fcf..3bee621a2 100644 --- a/src/cpp/benders/benders_core/include/BendersMathLogger.h +++ b/src/cpp/benders/benders_core/include/BendersMathLogger.h @@ -13,7 +13,16 @@ const std::string MATHLOGGERCONTEXT = "Benders"; enum class HEADERSTYPE { SHORT, LONG }; struct HeadersManager { explicit HeadersManager(HEADERSTYPE type, const BENDERSMETHOD& method); - std::vector headers_list; + + HEADERSTYPE type_; + BENDERSMETHOD method_; + + virtual std::vector HeadersList(); +}; +struct HeadersManagerExternalLoop : HeadersManager { + explicit HeadersManagerExternalLoop(HEADERSTYPE type, + const BENDERSMETHOD& method); + std::vector HeadersList() override; }; class LogDestination { @@ -38,6 +47,14 @@ template std::ostream& LogDestination::operator<<(const T& obj) { return (*stream_) << std::left << std::setw(width_) << obj; } +void PrintBendersData(LogDestination& log_destination, + const CurrentIterationData& data, const HEADERSTYPE& type, + const BENDERSMETHOD& method); + +void PrintExternalLoopData(LogDestination& log_destination, + const CurrentIterationData& data, + const HEADERSTYPE& type, + const BENDERSMETHOD& method); struct MathLoggerBehaviour : public ILoggerBenders { void write_header() { @@ -49,13 +66,17 @@ struct MathLoggerBehaviour : public ILoggerBenders { } virtual void display_message(const std::string& str) { - LogsDestination() << str; + LogsDestination() << str << std::endl; } virtual void Print(const CurrentIterationData& data) = 0; virtual std::vector Headers() const = 0; virtual LogDestination& LogsDestination() = 0; + + virtual void PrintIterationSeparatorBegin() override; + virtual void PrintIterationSeparatorEnd() override; virtual void setHeadersList() = 0; + virtual ~MathLoggerBehaviour() = default; }; struct MathLogger : public MathLoggerBehaviour { @@ -73,6 +94,7 @@ struct MathLogger : public MathLoggerBehaviour { virtual LogDestination& LogsDestination() { return log_destination_; } virtual void setHeadersList() = 0; HEADERSTYPE HeadersType() const { return type_; } + virtual ~MathLogger() = default; protected: void setHeadersList(const std::vector& headers); @@ -88,12 +110,27 @@ struct MathLoggerBase : public MathLogger { void Print(const CurrentIterationData& data) override; void setHeadersList() override; + virtual ~MathLoggerBase() = default; +}; + +struct MathLoggerBaseExternalLoop : public MathLoggerBase { + using MathLoggerBase::MathLoggerBase; + void Print(const CurrentIterationData& data) override; + void setHeadersList() override; + virtual ~MathLoggerBaseExternalLoop() = default; }; struct MathLoggerBendersByBatch : public MathLogger { using MathLogger::MathLogger; void Print(const CurrentIterationData& data) override; void setHeadersList() override; + virtual ~MathLoggerBendersByBatch() = default; +}; +struct MathLoggerBendersByBatchExternalLoop : public MathLoggerBendersByBatch { + using MathLoggerBendersByBatch::MathLoggerBendersByBatch; + void Print(const CurrentIterationData& data) override; + void setHeadersList() override; + virtual ~MathLoggerBendersByBatchExternalLoop() = default; }; class MathLoggerImplementation : public MathLoggerBehaviour { @@ -101,29 +138,21 @@ class MathLoggerImplementation : public MathLoggerBehaviour { explicit MathLoggerImplementation(const BENDERSMETHOD& method, const std::filesystem::path& file_path, std::streamsize width = 40, - HEADERSTYPE type = HEADERSTYPE::LONG) { - if (method == BENDERSMETHOD::BENDERS) { - implementation_ = - std::make_shared(file_path, width, type); - } else if (method == BENDERSMETHOD::BENDERSBYBATCH) { - implementation_ = - std::make_shared(file_path, width, type); - } - // else - } + HEADERSTYPE type = HEADERSTYPE::LONG); 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 } - } + HEADERSTYPE type = HEADERSTYPE::LONG); void Print(const CurrentIterationData& data) { implementation_->Print(data); } + void PrintIterationSeparatorBegin() override { + implementation_->PrintIterationSeparatorBegin(); + } + void PrintIterationSeparatorEnd() override { + implementation_->PrintIterationSeparatorEnd(); + } + virtual ~MathLoggerImplementation() = default; + protected: void setHeadersList() override { implementation_->setHeadersList(); } std::vector Headers() const override { @@ -144,6 +173,9 @@ class MathLoggerDriver : public ILoggerBenders { void display_message(const std::string& str) override; void add_logger(std::shared_ptr logger); void Print(const CurrentIterationData& data); + virtual void PrintIterationSeparatorBegin() override; + virtual void PrintIterationSeparatorEnd() override; + virtual ~MathLoggerDriver() = default; 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 d37a57be8..30318ab6b 100644 --- a/src/cpp/benders/benders_core/include/BendersStructsDatas.h +++ b/src/cpp/benders/benders_core/include/BendersStructsDatas.h @@ -5,6 +5,8 @@ #include "Worker.h" #include "common.h" +/*! \struct struct that hold current benders iteration + */ struct CurrentIterationData { double subproblems_walltime; double subproblems_cputime; @@ -36,7 +38,20 @@ struct CurrentIterationData { int cumulative_number_of_subproblem_solved; int min_simplexiter; int max_simplexiter; + // ugly + int benders_num_run; + double external_loop_criterion; }; + +// /*! \struct to store benders cuts data +// */ +// struct BendersCuts { +// Point x_cut; +// SubProblemDataMap subsProblemDataMap; +// }; + +// using BendersCutsPerIteration = std::vector; + /*! * \class WorkerMasterData * \brief Class use to store trace information during the algorithm run @@ -66,8 +81,8 @@ class WorkerMasterData { Point get_max_invest() const; }; -using WorkerMasterDataPtr = std::shared_ptr; struct BendersRelevantIterationsData { - WorkerMasterDataPtr last; - WorkerMasterDataPtr best; + WorkerMasterData last; + WorkerMasterData best; }; +using WorkerMasterDataVect = std::vector; diff --git a/src/cpp/benders/benders_core/include/SimulationOptions.h b/src/cpp/benders/benders_core/include/SimulationOptions.h index 69f8565f1..5d9c9316f 100644 --- a/src/cpp/benders/benders_core/include/SimulationOptions.h +++ b/src/cpp/benders/benders_core/include/SimulationOptions.h @@ -17,6 +17,7 @@ class SimulationOptions { void print(std::ostream &stream) const; BendersBaseOptions get_benders_options() const; BaseOptions get_base_options() const; + ExternalLoopOptions GetExternalLoopOptions() const; void write_default() const; Str2Dbl _weights; diff --git a/src/cpp/benders/benders_core/include/SimulationOptions.hxx b/src/cpp/benders/benders_core/include/SimulationOptions.hxx index df4f4487b..cd695af88 100644 --- a/src/cpp/benders/benders_core/include/SimulationOptions.hxx +++ b/src/cpp/benders/benders_core/include/SimulationOptions.hxx @@ -72,3 +72,13 @@ 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()) + +// EXTERNAL Loop Loss of Load thresold +BENDERS_OPTIONS_MACRO(EXT_LOOP_CRITERION_VALUE, double, 1.0, asDouble()) + +// EXTERNAL Loop epsilon +BENDERS_OPTIONS_MACRO(EXT_LOOP_CRITERION_TOLERANCE, double, 1e-1, asDouble()) + +// EXTERNAL Loop Max unsupplied energy per timestep +BENDERS_OPTIONS_MACRO(EXT_LOOP_CRITERION_COUNT_THRESHOLD, double, 1e-1, + asDouble()) diff --git a/src/cpp/benders/benders_core/include/SubproblemCut.h b/src/cpp/benders/benders_core/include/SubproblemCut.h index 58fef4899..ac85fbeba 100644 --- a/src/cpp/benders/benders_core/include/SubproblemCut.h +++ b/src/cpp/benders/benders_core/include/SubproblemCut.h @@ -4,21 +4,37 @@ #include "Worker.h" #include "common.h" +namespace PlainData { +struct Variables { + std::vector names; + std::vector values; + template + void serialize(Archive &ar, const unsigned int version) { + ar & names; + ar & values; + } +}; struct SubProblemData { double subproblem_cost; Point var_name_and_subgradient; + Variables variables; + double single_subpb_costs_under_approx; double subproblem_timer; int simplex_iter; int lpstatus; friend class boost::serialization::access; template void serialize(Archive &ar, const unsigned int version) { - ar &subproblem_cost; - ar &var_name_and_subgradient; - ar &subproblem_timer; - ar &simplex_iter; - ar &lpstatus; + ar & subproblem_cost; + ar & var_name_and_subgradient; + ar & variables; + ar & single_subpb_costs_under_approx; + ar & subproblem_timer; + ar & simplex_iter; + ar & lpstatus; } }; -using SubProblemDataMap = std::map; \ No newline at end of file +} // namespace PlainData + +using SubProblemDataMap = std::map; \ No newline at end of file diff --git a/src/cpp/benders/benders_core/include/SubproblemWorker.h b/src/cpp/benders/benders_core/include/SubproblemWorker.h index 9af7bcd0b..f84c80d93 100644 --- a/src/cpp/benders/benders_core/include/SubproblemWorker.h +++ b/src/cpp/benders/benders_core/include/SubproblemWorker.h @@ -22,6 +22,7 @@ class SubproblemWorker : public Worker { SolverLogManager&solver_log_manager, Logger logger); virtual ~SubproblemWorker() = default; + void get_solution(PlainData::Variables &vars) const; public: void fix_to(Point const &x0) const; diff --git a/src/cpp/benders/benders_core/include/Worker.h b/src/cpp/benders/benders_core/include/Worker.h index b41a5521c..4fdff708e 100644 --- a/src/cpp/benders/benders_core/include/Worker.h +++ b/src/cpp/benders/benders_core/include/Worker.h @@ -44,6 +44,25 @@ class Worker { void solve(int &lp_status, const std::string &outputroot, const std::string &output_master_mps_file_name, Writer writer) const; + int RowIndex(const std::string &row_name) const; + void ChangeRhs(int id_row, double val) const; + void GetRhs(double *val, int id_row) const; + void AddRows(std::vector const &qrtype_p, + std::vector const &rhs_p, + std::vector const &range_p, + std::vector const &mstart_p, + std::vector const &mclind_p, + std::vector const &dmatval_p, + const std::vector &row_names) const; + + /** + * @brief Returns the number of rows (constraints) + * + * @param solver_p : solver containing the model to consider. + */ + int Getnrows() const; + + int Getncols() const; public: SolverAbstract::Ptr _solver = diff --git a/src/cpp/benders/benders_core/include/common.h b/src/cpp/benders/benders_core/include/common.h index 3bf4c0dd1..3cfb2d115 100644 --- a/src/cpp/benders/benders_core/include/common.h +++ b/src/cpp/benders/benders_core/include/common.h @@ -49,7 +49,12 @@ typedef std::vector ActiveCutStorage; typedef std::pair mps_coupling; typedef std::list mps_coupling_list; -enum class BENDERSMETHOD { BENDERS, BENDERSBYBATCH }; +enum class BENDERSMETHOD { + BENDERS, + BENDERS_BY_BATCH, + BENDERS_EXTERNAL_LOOP, + BENDERS_BY_BATCH_EXTERNAL_LOOP +}; struct Predicate { bool operator()(PointPtr const &lhs, PointPtr const &rhs) const { @@ -161,6 +166,12 @@ struct BendersBaseOptions : public BaseOptions { size_t BATCH_SIZE; }; +struct ExternalLoopOptions { + double EXT_LOOP_CRITERION_VALUE = 1.0; + double EXT_LOOP_CRITERION_TOLERANCE = 1e-1; + double EXT_LOOP_CRITERION_COUNT_THRESHOLD = 1e-1; +}; + 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 0c9b3b2bf..bf2859bc7 100644 --- a/src/cpp/benders/benders_mpi/BendersMPI.cpp +++ b/src/cpp/benders/benders_mpi/BendersMPI.cpp @@ -40,6 +40,7 @@ void BendersMpi::InitializeProblems() { } current_problem_id++; } + init_problems_ = false; } void BendersMpi::BuildMasterProblem() { if (_world.rank() == rank_0) { @@ -152,8 +153,21 @@ SubProblemDataMap BendersMpi::get_subproblem_cut_package() { void BendersMpi::master_build_cuts( std::vector gathered_subproblem_map) { SetSubproblemCost(0); + + // if (Rank() == rank_0) { + // TODO decoment to save all cuts + // workerMasterDataVect_.push_back({_data.x_cut, {}}); + // may be unuseful + // current_iteration_cuts_.x_cut = _data.x_cut; + // } for (const auto &subproblem_data_map : gathered_subproblem_map) { - for (auto &&[_, subproblem_data] : subproblem_data_map) { + for (auto &&[sub_problem_name, subproblem_data] : subproblem_data_map) { + // save current cuts + // workerMasterDataVect_.back().subsProblemDataMap[sub_problem_name] = + // subproblem_data; + + // current_iteration_cuts_.subsProblemDataMap[sub_problem_name] = + // subproblem_data; SetSubproblemCost(GetSubproblemCost() + subproblem_data.subproblem_cost); // compute delta_cut >= options.CUT_MASTER_TOL; BoundSimplexIterations(subproblem_data.simplex_iter); @@ -163,9 +177,11 @@ void BendersMpi::master_build_cuts( _logger->display_message("\tSolving subproblems..."); _data.ub = 0; + for (const auto &subproblem_data_map : gathered_subproblem_map) { BuildCutFull(subproblem_data_map); } + _logger->LogSubproblemsSolvingCumulativeCpuTime( GetSubproblemsCumulativeCpuTime()); _logger->LogSubproblemsSolvingWalltime(GetSubproblemsWalltime()); @@ -226,13 +242,19 @@ void BendersMpi::free() { * */ void BendersMpi::Run() { - PreRunInitialization(); + if (init_data_) { + PreRunInitialization(); + } else { + // only ? + _data.stop = false; + } _data.number_of_subproblem_solved = _data.nsubproblem; while (!_data.stop) { ++_data.it; ResetSimplexIterationsBounds(); - /*Solve Master problem, get optimal value and cost and send it to process*/ + /*Solve Master problem, get optimal value and cost and send it to + * process*/ step_1_solve_master(); /*Gather cut from each subproblem in master thread and add them to Master @@ -261,6 +283,7 @@ void BendersMpi::Run() { } _world.barrier(); } + void BendersMpi::PreRunInitialization() { init_data(); @@ -281,17 +304,22 @@ void BendersMpi::PreRunInitialization() { } } mathLoggerDriver_->write_header(); + init_data_ = false; } void BendersMpi::launch() { - InitializeProblems(); + ++_data.benders_num_run; + if (init_problems_) { + InitializeProblems(); + } _world.barrier(); Run(); _world.barrier(); post_run_actions(); - - free(); + if (free_problems_) { + free(); + } _world.barrier(); } diff --git a/src/cpp/benders/external_loop/CMakeLists.txt b/src/cpp/benders/external_loop/CMakeLists.txt new file mode 100644 index 000000000..68c3c1293 --- /dev/null +++ b/src/cpp/benders/external_loop/CMakeLists.txt @@ -0,0 +1,40 @@ +# =========================================================================== +# CMake configuration +# =========================================================================== + +# =========================================================================== +# Targets +# =========================================================================== + +find_package(TBB REQUIRED CONFIG) +if (TBB_VERSION_MAJOR VERSION_LESS "2018") + message(FATAL_ERROR "Require tbb 2018 or higher.") +endif() +if (TBB_VERSION_MAJOR VERSION_GREATER "2020") + message(FATAL_ERROR "Require tbb 2018 to 2020.") +endif() + +add_library (external_loop STATIC + ${CMAKE_CURRENT_SOURCE_DIR}/OuterLoop.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/OuterloopCriterion.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/MasterUpdateBase.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/CutsManagement.cpp + ) + + + + +target_include_directories (external_loop + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +target_link_libraries (external_loop + PUBLIC + helpers + benders_core + benders_mpi_core + benders_by_batch_core +) + +add_library (${PROJECT_NAME}::external_loop ALIAS external_loop) \ No newline at end of file diff --git a/src/cpp/benders/external_loop/CutsManagement.cpp b/src/cpp/benders/external_loop/CutsManagement.cpp new file mode 100644 index 000000000..a0fd00625 --- /dev/null +++ b/src/cpp/benders/external_loop/CutsManagement.cpp @@ -0,0 +1,7 @@ +#include "CutsManagement.h" + +void CutsManagerRunTime::Save(const WorkerMasterDataVect& benders_cuts) { + benders_cuts_ = benders_cuts; +} + +WorkerMasterDataVect CutsManagerRunTime::Load() { return benders_cuts_; } diff --git a/src/cpp/benders/external_loop/MasterUpdateBase.cpp b/src/cpp/benders/external_loop/MasterUpdateBase.cpp new file mode 100644 index 000000000..2217dc88a --- /dev/null +++ b/src/cpp/benders/external_loop/MasterUpdateBase.cpp @@ -0,0 +1,112 @@ +#include "MasterUpdate.h" + +MasterUpdateBase::MasterUpdateBase(pBendersBase benders, double tau) + : benders_(std::move(benders)), lambda_(0), lambda_min_(0) { + CheckTau(tau); +} + +MasterUpdateBase::MasterUpdateBase(pBendersBase benders, double tau, + const std::string &name) + : MasterUpdateBase(benders, tau) { + min_invest_constraint_name_ = name; +} +MasterUpdateBase::MasterUpdateBase(pBendersBase benders, double lambda, + double lambda_min, double lambda_max, + double tau) + : benders_(std::move(benders)), + lambda_(lambda), + lambda_min_(lambda_min), + lambda_max_(lambda_max) { + CheckTau(tau); +} + +MasterUpdateBase::MasterUpdateBase(pBendersBase benders, double lambda, + double lambda_min, double lambda_max, + double tau, const std::string &name) + : MasterUpdateBase(benders, lambda, lambda_min, lambda_max, tau) { + min_invest_constraint_name_ = name; +} + +void MasterUpdateBase::CheckTau(double tau) { + if (tau >= 0 && tau <= 1) { + // TODO log + dichotomy_weight_coeff_ = tau; + } +} + +void MasterUpdateBase::Init() { + // check lambda_max_ + if (lambda_max_ <= 0 || lambda_max_ < lambda_min_) { + // TODO log + SetLambdaMaxToMaxInvestmentCosts(); + } +} +void MasterUpdateBase::SetLambdaMaxToMaxInvestmentCosts() { + const auto &obj = benders_->MasterObjectiveFunctionCoeffs(); + const auto max_invest = + benders_->BestIterationWorkerMaster().get_max_invest(); + lambda_max_ = 0; + for (const auto &[var_name, var_id] : benders_->MasterVariables()) { + lambda_max_ += obj[var_id] * max_invest.at(var_name); + } +} +void MasterUpdateBase::Update(const CRITERION &criterion) { + switch (criterion) { + case CRITERION::LOW: + lambda_max_ = + std::min(lambda_max_, benders_->GetBestIterationData().invest_cost); + break; + case CRITERION::HIGH: + lambda_min_ = lambda_; + break; + + default: + return; + } + lambda_ = dichotomy_weight_coeff_ * lambda_max_ + + (1 - dichotomy_weight_coeff_) * lambda_min_; + UpdateConstraints(); +} + +void MasterUpdateBase::UpdateConstraints() { + if (!benders_->MasterIsEmpty() && additional_constraint_index_ > -1) { + benders_->MasterChangeRhs(additional_constraint_index_, lambda_); + + } else { + AddMinInvestConstraint(); + } +} + +/** + * Add the new constraint in benders main problem + * /!\ to be called once + */ +void MasterUpdateBase::AddMinInvestConstraint() { + auto master_variables = benders_->MasterVariables(); + const auto obj_coeff = benders_->MasterObjectiveFunctionCoeffs(); + auto newnz = master_variables.size(); + int newrows = 1; + std::vector rtype(newrows, 'G'); + std::vector rhs(newrows, lambda_); + std::vector mclind(newnz); + + size_t mclindCnt_l(0); + std::vector matval(newnz); + for (auto const &[name, var_id] : master_variables) { + mclind[mclindCnt_l] = var_id; + matval[mclindCnt_l] = obj_coeff.at(var_id); + ++mclindCnt_l; + } + std::vector matstart(newrows + 1); + matstart[0] = 0; + matstart[1] = newnz; + if (!min_invest_constraint_name_.empty()) { + std::vector row_names(newrows, min_invest_constraint_name_); + + benders_->MasterAddRows(rtype, rhs, {}, matstart, mclind, matval, + row_names); + } else { + benders_->MasterAddRows(rtype, rhs, {}, matstart, mclind, matval); + } + additional_constraint_index_ = benders_->MasterGetnrows() - 1; +} diff --git a/src/cpp/benders/external_loop/OuterLoop.cpp b/src/cpp/benders/external_loop/OuterLoop.cpp new file mode 100644 index 000000000..f86ca3d92 --- /dev/null +++ b/src/cpp/benders/external_loop/OuterLoop.cpp @@ -0,0 +1,90 @@ +#include "OuterLoop.h" + +#include "LoggerUtils.h" + +OuterLoop::OuterLoop(std::shared_ptr criterion, + std::shared_ptr master_updater, + std::shared_ptr cuts_manager, + pBendersBase benders, mpi::environment& env, + mpi::communicator& world) + : criterion_(std::move(criterion)), + master_updater_(std::move(master_updater)), + cuts_manager_(std::move(cuts_manager)), + benders_(std::move(benders)), + env_(env), + world_(world) { + loggers_.AddLogger(benders_->_logger); + loggers_.AddLogger(benders_->mathLoggerDriver_); +} + +void OuterLoop::Run() { + benders_->DoFreeProblems(false); + benders_->InitializeProblems(); + benders_->InitExternalValues(); + CRITERION criterion = CRITERION::IS_MET; + std::vector obj_coeff; + if (world_.rank() == 0) { + obj_coeff = benders_->MasterObjectiveFunctionCoeffs(); + + // /!\ partially + benders_->SetMasterObjectiveFunctionCoeffsToZeros(); + + PrintLog(); + } + benders_->launch(); + if (world_.rank() == 0) { + benders_->SetMasterObjectiveFunction(obj_coeff.data(), 0, + obj_coeff.size() - 1); + // de-comment for general case + // cuts_manager_->Save(benders_->AllCuts()); + // auto cuts = cuts_manager_->Load(); + criterion = + criterion_->IsCriterionSatisfied(benders_->BestIterationWorkerMaster()); + if (criterion == CRITERION::HIGH) { + std::ostringstream err_msg; + err_msg << PrefixMessage(LogUtils::LOGLEVEL::FATAL, "External Loop") + << "Criterion cannot be satisfied for your study:\n" + << criterion_->StateAsString(); + throw CriterionCouldNotBeSatisfied(err_msg.str(), LOGLOCATION); + } + // lambda_max + master_updater_->Init(); + } + + mpi::broadcast(world_, criterion, 0); + + while (criterion != CRITERION::IS_MET) { + benders_->ResetData(criterion_->CriterionValue()); + PrintLog(); + benders_->launch(); + if (world_.rank() == 0) { + criterion = criterion_->IsCriterionSatisfied( + benders_->BestIterationWorkerMaster()); + master_updater_->Update(criterion); + } + + mpi::broadcast(world_, criterion, 0); + } + // last prints + PrintLog(); + auto benders_data = benders_->GetCurrentIterationData(); + benders_data.external_loop_criterion = criterion_->CriterionValue(); + benders_->mathLoggerDriver_->Print(benders_data); + + // TODO general-case + // cuts_manager_->Save(benders_->AllCuts()); + benders_->free(); +} + +void OuterLoop::PrintLog() { + std::ostringstream msg; + auto logger = benders_->_logger; + logger->PrintIterationSeparatorBegin(); + msg << "*** Outer loop: " << benders_->GetBendersRunNumber(); + logger->display_message(msg.str()); + msg.str(""); + msg << "*** Criterion value: " << std::scientific << std::setprecision(10) + << criterion_->CriterionValue(); + logger->display_message(msg.str()); + logger->PrintIterationSeparatorEnd(); +} \ No newline at end of file diff --git a/src/cpp/benders/external_loop/OuterloopCriterion.cpp b/src/cpp/benders/external_loop/OuterloopCriterion.cpp new file mode 100644 index 000000000..842546b7a --- /dev/null +++ b/src/cpp/benders/external_loop/OuterloopCriterion.cpp @@ -0,0 +1,49 @@ +#include "OuterLoopCriterion.h" + +#include "LoggerUtils.h" + +OuterloopCriterionLossOfLoad::OuterloopCriterionLossOfLoad( + const ExternalLoopOptions& options) + : options_(options) {} + +CRITERION OuterloopCriterionLossOfLoad::IsCriterionSatisfied( + const WorkerMasterData& worker_master_data) { + ProcessSum(worker_master_data); + + if (sum_loss_ <= options_.EXT_LOOP_CRITERION_VALUE + + options_.EXT_LOOP_CRITERION_TOLERANCE) { + if (sum_loss_ >= options_.EXT_LOOP_CRITERION_VALUE - + options_.EXT_LOOP_CRITERION_TOLERANCE) { + return CRITERION::IS_MET; + } + return CRITERION::LOW; + } else { + return CRITERION::HIGH; + } +} + +void OuterloopCriterionLossOfLoad::ProcessSum( + const WorkerMasterData& worker_master_data) { + sum_loss_ = 0; + for (const auto& [sub_problem_name, sub_problem_data] : + worker_master_data._cut_trace) { + for (auto i(0); i < sub_problem_data.variables.names.size(); ++i) { + auto var_name = sub_problem_data.variables.names[i]; + auto solution = sub_problem_data.variables.values[i]; + if (std::regex_search(var_name, rgx_) && + solution > options_.EXT_LOOP_CRITERION_COUNT_THRESHOLD) { + // 1h of unsupplied energy + sum_loss_ += 1; + } + } + } +} + +std::string OuterloopCriterionLossOfLoad::StateAsString() const { + std::ostringstream msg; + msg << "Sum loss = " << sum_loss_ << "\n" + << "threshold: " << options_.EXT_LOOP_CRITERION_VALUE << "\n" + << "epsilon: " << options_.EXT_LOOP_CRITERION_TOLERANCE << "\n"; + + return msg.str(); +} diff --git a/src/cpp/benders/external_loop/include/CutsManagement.h b/src/cpp/benders/external_loop/include/CutsManagement.h new file mode 100644 index 000000000..5a1addfcd --- /dev/null +++ b/src/cpp/benders/external_loop/include/CutsManagement.h @@ -0,0 +1,18 @@ +#pragma once +#include "BendersStructsDatas.h" + +class ICutsManager { + public: + ICutsManager() = default; + void virtual Save(const WorkerMasterDataVect& benders_cuts) = 0; + virtual WorkerMasterDataVect Load() = 0; +}; + +class CutsManagerRunTime : public ICutsManager { + public: + void Save(const WorkerMasterDataVect& benders_cuts) override; + WorkerMasterDataVect Load() override; + + private: + WorkerMasterDataVect benders_cuts_; +}; diff --git a/src/cpp/benders/external_loop/include/MasterUpdate.h b/src/cpp/benders/external_loop/include/MasterUpdate.h new file mode 100644 index 000000000..51f3a746b --- /dev/null +++ b/src/cpp/benders/external_loop/include/MasterUpdate.h @@ -0,0 +1,37 @@ +#pragma once +#include "OuterLoopCriterion.h" + +class IMasterUpdate { + public: + virtual void Update(const CRITERION &criterion) = 0; + virtual void Init() = 0; +}; + +class MasterUpdateBase : public IMasterUpdate { + public: + explicit MasterUpdateBase(pBendersBase benders, double lambda, + double lambda_min, double lambda_max, double tau); + explicit MasterUpdateBase(pBendersBase benders, double lambda, + double lambda_min, double lambda_max, double tau, + const std::string &name); + explicit MasterUpdateBase(pBendersBase benders, double tau, + const std::string &name); + explicit MasterUpdateBase(pBendersBase benders, double tau); + void Update(const CRITERION &criterion) override; + void Init() override; + + private: + void CheckTau(double tau); + void SetLambdaMaxToMaxInvestmentCosts(); + void UpdateConstraints(); + void AddMinInvestConstraint(); + // rename min invest constraint + std::string min_invest_constraint_name_ = "Min_Investment_Constraint"; + int additional_constraint_index_ = -1; + pBendersBase benders_; + double lambda_ = 0; + double lambda_min_ = 0; + double lambda_max_ = -1; + // tau + double dichotomy_weight_coeff_ = 0.5; +}; diff --git a/src/cpp/benders/external_loop/include/OuterLoop.h b/src/cpp/benders/external_loop/include/OuterLoop.h new file mode 100644 index 000000000..080c1f65f --- /dev/null +++ b/src/cpp/benders/external_loop/include/OuterLoop.h @@ -0,0 +1,25 @@ +#pragma once +#include "CutsManagement.h" +#include "MasterUpdate.h" +#include "OuterLoopCriterion.h" +#include "common_mpi.h" + +class OuterLoop { + public: + explicit OuterLoop(std::shared_ptr criterion, + std::shared_ptr master_updater, + std::shared_ptr cuts_manager, + pBendersBase benders, mpi::environment& env, + mpi::communicator& world); + void Run(); + + private: + void PrintLog(); + std::shared_ptr criterion_; + std::shared_ptr master_updater_; + std::shared_ptr cuts_manager_; + pBendersBase benders_; + BendersLoggerBase loggers_; + mpi::environment& env_; + mpi::communicator& world_; +}; \ No newline at end of file diff --git a/src/cpp/benders/external_loop/include/OuterLoopCriterion.h b/src/cpp/benders/external_loop/include/OuterLoopCriterion.h new file mode 100644 index 000000000..128380873 --- /dev/null +++ b/src/cpp/benders/external_loop/include/OuterLoopCriterion.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include + +#include "BendersBase.h" +#include "LogUtils.h" +#include "common.h" + +class CriterionCouldNotBeSatisfied + : public LogUtils::XpansionError { + using LogUtils::XpansionError::XpansionError; +}; + +enum class CRITERION { LOW, IS_MET, HIGH }; +class IOuterLoopCriterion { + public: + virtual CRITERION IsCriterionSatisfied( + const WorkerMasterData& worker_master_data) = 0; + virtual std::string StateAsString() const = 0; + virtual double CriterionValue() const = 0; +}; + +class OuterloopCriterionLossOfLoad : public IOuterLoopCriterion { + public: + explicit OuterloopCriterionLossOfLoad(const ExternalLoopOptions& options); + CRITERION IsCriterionSatisfied( + const WorkerMasterData& milp_solution) override; + std::string StateAsString() const override; + double CriterionValue() const override { return sum_loss_; } + + private: + void ProcessSum(const WorkerMasterData& worker_master_data); + const std::string positive_unsupplied_vars_prefix_ = + "^PositiveUnsuppliedEnergy::"; + const std::regex rgx_ = std::regex(positive_unsupplied_vars_prefix_); + + ExternalLoopOptions options_; + double sum_loss_ = 0.0; +}; diff --git a/src/cpp/benders/factories/BendersFactory.cpp b/src/cpp/benders/factories/BendersFactory.cpp index db55338cb..6231a1f7b 100644 --- a/src/cpp/benders/factories/BendersFactory.cpp +++ b/src/cpp/benders/factories/BendersFactory.cpp @@ -1,9 +1,14 @@ #include +#include "BendersByBatch.h" +#include "BendersSequential.h" +#include "ILogger.h" #include "BendersFactory.h" #include "LogUtils.h" #include "LoggerFactories.h" +#include "OuterLoop.h" +#include "OutputWriter.h" #include "StartUp.h" #include "Timer.h" #include "Worker.h" @@ -11,103 +16,133 @@ #include "gflags/gflags.h" #include "glog/logging.h" -BENDERSMETHOD DeduceBendersMethod(size_t coupling_map_size, size_t batch_size) { - auto method = (batch_size == 0 || batch_size == coupling_map_size - 1) - ? BENDERSMETHOD::BENDERS - : BENDERSMETHOD::BENDERSBYBATCH; - - return method; +BENDERSMETHOD DeduceBendersMethod(size_t coupling_map_size, size_t batch_size, + bool external_loop) { + /* + classical benders: 0*100 + 0*10 = 0 + classical benders + external loop: 0*100 + 1*10 = 10 + benders by batch: 1*100 + 0*10 = 100 + benders by batch + external loop: 1*100 + 1*10 = 110 + */ + + auto benders_algo_score = + (batch_size == 0 || batch_size == coupling_map_size - 1) ? 0 : 1; + auto external_loop_score = external_loop ? 1 : 0; + auto total_score = 100 * benders_algo_score + 10 * external_loop_score; + switch (total_score) { + case 0: + default: + return BENDERSMETHOD::BENDERS; + case 10: + return BENDERSMETHOD::BENDERS_EXTERNAL_LOOP; + case 100: + return BENDERSMETHOD::BENDERS_BY_BATCH; + case 110: + return BENDERSMETHOD::BENDERS_BY_BATCH_EXTERNAL_LOOP; + } } -int RunBenders(char** argv, const std::filesystem::path& options_file, - mpi::environment& env, mpi::communicator& world) { - // Read options, needed to have options.OUTPUTROOT - BendersLoggerBase benders_loggers; +pBendersBase PrepareForExecution(BendersLoggerBase& benders_loggers, + const SimulationOptions& options, + const char* argv0, bool external_loop, + mpi::environment& env, + mpi::communicator& world) { + pBendersBase benders; Logger logger; std::shared_ptr math_log_driver; - try { - /* code */ + BendersBaseOptions benders_options(options.get_benders_options()); - SimulationOptions options(options_file); + google::InitGoogleLogging(argv0); + auto path_to_log = + std::filesystem::path(options.OUTPUTROOT) / + ("bendersLog-rank" + std::to_string(world.rank()) + ".txt."); + google::SetLogDestination(google::GLOG_INFO, path_to_log.string().c_str()); - BendersBaseOptions benders_options(options.get_benders_options()); - - google::InitGoogleLogging(argv[0]); - auto path_to_log = - std::filesystem::path(options.OUTPUTROOT) / - ("bendersLog-rank" + std::to_string(world.rank()) + ".txt."); - google::SetLogDestination(google::GLOG_INFO, path_to_log.string().c_str()); - - 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; - const auto coupling_map = build_input(benders_options.STRUCTURE_FILE); - const auto method = - DeduceBendersMethod(coupling_map.size(), options.BATCH_SIZE); - - if (world.rank() == 0) { - auto benders_log_console = benders_options.LOG_LEVEL > 0; - auto logger_factory = - FileAndStdoutLoggerFactory(log_reports_name, benders_log_console); - auto math_log_factory = - MathLoggerFactory(method, benders_log_console, 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)) - return 0; - } else { - logger = build_void_logger(); - writer = build_void_writer(); - math_log_driver = MathLoggerFactory::get_void_logger(); - } + auto log_reports_name = + std::filesystem::path(options.OUTPUTROOT) / "reportbenders.txt"; - benders_loggers.AddLogger(logger); - benders_loggers.AddLogger(math_log_driver); - pBendersBase benders; - switch (method) { - case BENDERSMETHOD::BENDERS: - benders = std::make_shared(benders_options, logger, writer, - env, world, math_log_driver); - break; - case BENDERSMETHOD::BENDERSBYBATCH: - benders = std::make_shared( - benders_options, logger, writer, env, world, math_log_driver); - break; - } + auto math_logs_file = + std::filesystem::path(options.OUTPUTROOT) / "benders_solver.log"; + + Writer writer; + const auto coupling_map = build_input(benders_options.STRUCTURE_FILE); + const auto method = DeduceBendersMethod(coupling_map.size(), + options.BATCH_SIZE, external_loop); - 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 (world.rank() == 0) { + auto benders_log_console = benders_options.LOG_LEVEL > 0; + auto logger_factory = + FileAndStdoutLoggerFactory(log_reports_name, benders_log_console); + auto math_log_factory = + MathLoggerFactory(method, benders_log_console, 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)) + return nullptr; + } else { + logger = build_void_logger(); + writer = build_void_writer(); + math_log_driver = MathLoggerFactory::get_void_logger(); + } - if (benders_options.LOG_LEVEL > 1) { - auto solver_log = std::filesystem::path(options.OUTPUTROOT) / - (std::string("solver_log_proc_") + - std::to_string(world.rank()) + ".txt"); + benders_loggers.AddLogger(logger); + benders_loggers.AddLogger(math_log_driver); + switch (method) { + case BENDERSMETHOD::BENDERS: + case BENDERSMETHOD::BENDERS_EXTERNAL_LOOP: + benders = std::make_shared(benders_options, logger, writer, + env, world, math_log_driver); + break; + case BENDERSMETHOD::BENDERS_BY_BATCH: + case BENDERSMETHOD::BENDERS_BY_BATCH_EXTERNAL_LOOP: + benders = std::make_shared( + benders_options, logger, writer, env, world, 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()); - benders->set_solver_log_file(solver_log); - } - writer->write_log_level(options.LOG_LEVEL); - writer->write_master_name(options.MASTER_NAME); - writer->write_solver_name(options.SOLVER_NAME); - benders->launch(); + if (benders_options.LOG_LEVEL > 1) { + auto solver_log = std::filesystem::path(options.OUTPUTROOT) / + (std::string("solver_log_proc_") + + std::to_string(world.rank()) + ".txt"); - std::stringstream str; - str << "Optimization results available in : " << options.JSON_FILE - << std::endl; - benders_loggers.display_message(str.str()); + benders->set_solver_log_file(solver_log); + } + writer->write_log_level(options.LOG_LEVEL); + writer->write_master_name(options.MASTER_NAME); + writer->write_solver_name(options.SOLVER_NAME); + return benders; +} - str.str(""); - str << "Benders ran in " << benders->execution_time() << " s" << std::endl; - benders_loggers.display_message(str.str()); +int RunBenders(char** argv, const std::filesystem::path& options_file, + mpi::environment& env, mpi::communicator& world) { + // Read options, needed to have options.OUTPUTROOT + BendersLoggerBase benders_loggers; + + try { + SimulationOptions options(options_file); + auto benders = PrepareForExecution(benders_loggers, options, argv[0], false, + env, world); + if (benders) { + benders->launch(); + + std::stringstream str; + str << "Optimization results available in : " << options.JSON_FILE + << std::endl; + benders_loggers.display_message(str.str()); + + str.str(""); + str << "Benders ran in " << benders->execution_time() << " s" + << std::endl; + benders_loggers.display_message(str.str()); + } } catch (std::exception& e) { std::ostringstream msg; @@ -124,6 +159,42 @@ int RunBenders(char** argv, const std::filesystem::path& options_file, } return 0; } +int RunExternalLoop_(char** argv, const std::filesystem::path& options_file, + mpi::environment& env, mpi::communicator& world) { + BendersLoggerBase benders_loggers; + + try { + SimulationOptions options(options_file); + auto benders = PrepareForExecution(benders_loggers, options, argv[0], true, + env, world); + double tau = 0.5; + std::shared_ptr criterion = + std::make_shared( + options.GetExternalLoopOptions()); + std::shared_ptr master_updater = + std::make_shared(benders, tau); + std::shared_ptr cuts_manager = + std::make_shared(); + + OuterLoop ext_loop(criterion, master_updater, cuts_manager, benders, env, + world); + ext_loop.Run(); + + } catch (std::exception& e) { + std::ostringstream msg; + msg << "error: " << e.what() << std::endl; + benders_loggers.display_message(msg.str()); + mpi::environment::abort(1); + return 1; + } catch (...) { + std::ostringstream msg; + msg << "Exception of unknown type!" << std::endl; + benders_loggers.display_message(msg.str()); + mpi::environment::abort(1); + return 1; + } + return 0; +} BendersMainFactory::BendersMainFactory(int argc, char** argv, @@ -137,6 +208,7 @@ BendersMainFactory::BendersMainFactory(int argc, char** argv, options_file_ = std::filesystem::path(argv_[1]); } + BendersMainFactory::BendersMainFactory( int argc, char** argv, const std::filesystem::path& options_file, mpi::environment& env, mpi::communicator& world) @@ -146,6 +218,11 @@ BendersMainFactory::BendersMainFactory( usage(argc); } } + int BendersMainFactory::Run() const { return RunBenders(argv_, options_file_, *penv_, *pworld_); } + +int BendersMainFactory::RunExternalLoop() const { + return RunExternalLoop_(argv_, options_file_, *penv_, *pworld_); +} diff --git a/src/cpp/benders/factories/CMakeLists.txt b/src/cpp/benders/factories/CMakeLists.txt index e9cdc6a3b..188101411 100644 --- a/src/cpp/benders/factories/CMakeLists.txt +++ b/src/cpp/benders/factories/CMakeLists.txt @@ -12,6 +12,7 @@ target_link_libraries (factories output_core logger_lib ${PROJECT_NAME}::benders_mpi_core + external_loop ) target_include_directories (factories diff --git a/src/cpp/benders/factories/include/BendersFactory.h b/src/cpp/benders/factories/include/BendersFactory.h index 54ab0c415..4ec0dd4b4 100644 --- a/src/cpp/benders/factories/include/BendersFactory.h +++ b/src/cpp/benders/factories/include/BendersFactory.h @@ -1,10 +1,7 @@ #ifndef ANTARES_XPANSION_SRC_CPP_BENDERS_FACTORIES_INCLUDE_BENDERSFACTORY_H #define ANTARES_XPANSION_SRC_CPP_BENDERS_FACTORIES_INCLUDE_BENDERSFACTORY_H -#include "BendersByBatch.h" #include "BendersMPI.h" -#include "BendersSequential.h" -#include "ILogger.h" -#include "OutputWriter.h" +#include "common.h" class BendersMainFactory { private: @@ -22,5 +19,6 @@ class BendersMainFactory { boost::mpi::environment& env, boost::mpi::communicator& world); int Run() const; + int RunExternalLoop() const; }; #endif // ANTARES_XPANSION_SRC_CPP_BENDERS_FACTORIES_INCLUDE_BENDERSFACTORY_H \ No newline at end of file diff --git a/src/cpp/benders/logger/Master.cpp b/src/cpp/benders/logger/Master.cpp index d2e0236b1..fc0dde8c6 100644 --- a/src/cpp/benders/logger/Master.cpp +++ b/src/cpp/benders/logger/Master.cpp @@ -112,5 +112,16 @@ void Master::cumulative_number_of_sub_problem_solved(int number) { } } +void Master::PrintIterationSeparatorBegin() { + for (auto logger : _loggers) { + logger->PrintIterationSeparatorBegin(); + } +} +void Master::PrintIterationSeparatorEnd() { + for (auto logger : _loggers) { + logger->PrintIterationSeparatorEnd(); + } +} + } // namespace logger } // namespace xpansion diff --git a/src/cpp/benders/logger/MathLogger.cpp b/src/cpp/benders/logger/MathLogger.cpp index c5410cd3c..b48c5e955 100644 --- a/src/cpp/benders/logger/MathLogger.cpp +++ b/src/cpp/benders/logger/MathLogger.cpp @@ -1,81 +1,25 @@ #include "logger/MathLogger.h" -#include -#include - -double getDurationNotSolving(double iteration, double master, - double subproblems) { - return iteration - master - subproblems; -} - -void MathLoggerBase::setHeadersList() { - auto type = HeadersType(); - HeadersManager headers_manager(type, BENDERSMETHOD::BENDERS); - MathLogger::setHeadersList(headers_manager.headers_list); -} +MathLoggerFile::MathLoggerFile(const BENDERSMETHOD& method, + const std::filesystem::path& filename, + std::streamsize width) + : MathLoggerImplementation(method, filename, width, HEADERSTYPE::LONG) {} -void MathLogger::setHeadersList(const std::vector& headers) { - headers_.clear(); - headers_ = headers; +void MathLoggerFile::display_message(const std::string& msg) { + // keep empty } -void PrintData(LogDestination& log_destination, - const CurrentIterationData& data, const HEADERSTYPE& type, - const BENDERSMETHOD& method) { - log_destination << data.it; - log_destination << std::scientific << std::setprecision(10) << data.lb; - if (method == BENDERSMETHOD::BENDERS) { - log_destination << std::scientific << std::setprecision(10) << data.ub; - log_destination << std::scientific << std::setprecision(10) << data.best_ub; - log_destination << std::scientific << std::setprecision(2) - << data.best_ub - data.lb; - log_destination << std::scientific << std::setprecision(2) - << (data.best_ub - data.lb) / data.best_ub; - } - log_destination << data.min_simplexiter; - log_destination << data.max_simplexiter; - if (type == HEADERSTYPE::LONG || method == BENDERSMETHOD::BENDERSBYBATCH) { - log_destination << data.number_of_subproblem_solved; - } - if (type == HEADERSTYPE::LONG) { - log_destination << data.cumulative_number_of_subproblem_solved; - } - - log_destination << std::setprecision(2) << data.iteration_time; - log_destination << std::setprecision(2) << data.timer_master; - log_destination << std::setprecision(2) << data.subproblems_walltime; - - if (type == HEADERSTYPE::LONG) { - log_destination << std::setprecision(2) - << data.subproblems_cumulative_cputime; - log_destination << std::setprecision(2) - << getDurationNotSolving(data.iteration_time, - data.timer_master, - data.subproblems_walltime); - } - log_destination << std::endl; -} -void MathLoggerBase::Print(const CurrentIterationData& data) { - PrintData(LogsDestination(), data, HeadersType(), BENDERSMETHOD::BENDERS); +void MathLoggerOstream::PrintIterationSeparatorBegin() { + // keep empty } -void MathLoggerBendersByBatch::setHeadersList() { - auto type = HeadersType(); - HeadersManager headers_manager(type, BENDERSMETHOD::BENDERSBYBATCH); - - MathLogger::setHeadersList(headers_manager.headers_list); +void MathLoggerOstream::PrintIterationSeparatorEnd() { + // keep empty } - -void MathLoggerBendersByBatch::Print(const CurrentIterationData& data) { - PrintData(LogsDestination(), data, HeadersType(), - BENDERSMETHOD::BENDERSBYBATCH); +void MathLoggerFile::PrintIterationSeparatorBegin() { + // keep empty } -MathLoggerFile::MathLoggerFile(const BENDERSMETHOD& method, - const std::filesystem::path& filename, - std::streamsize width) - : MathLoggerImplementation(method, filename, width, HEADERSTYPE::LONG) {} - -void MathLoggerFile::display_message(const std::string& msg) { +void MathLoggerFile::PrintIterationSeparatorEnd() { // keep empty -} \ No newline at end of file +} diff --git a/src/cpp/benders/logger/User.cpp b/src/cpp/benders/logger/User.cpp index 087899a4a..9e1e080dc 100644 --- a/src/cpp/benders/logger/User.cpp +++ b/src/cpp/benders/logger/User.cpp @@ -90,6 +90,20 @@ void User::display_restart_message() { _stream << PrefixMessage(LogUtils::LOGLEVEL::INFO, CONTEXT) << "Restart Study..." << std::endl; } + +void User::PrintIterationSeparatorBegin() { + _stream << PrefixMessage(LogUtils::LOGLEVEL::INFO, CONTEXT); + std::string sep_msg("/*\\"); + sep_msg += std::string(74, '-'); + _stream << sep_msg << std::endl; +} +void User::PrintIterationSeparatorEnd() { + _stream << PrefixMessage(LogUtils::LOGLEVEL::INFO, CONTEXT); + std::string sep_msg(74, '-'); + sep_msg = "\\*/" + sep_msg; + _stream << sep_msg << std::endl; +} + void User::restart_elapsed_time(const double elapsed_time) { _stream << PrefixMessage(LogUtils::LOGLEVEL::INFO, CONTEXT) << indent_1 << "Elapsed time: " << format_time_str(elapsed_time) << std::endl; diff --git a/src/cpp/benders/logger/UserFile.cpp b/src/cpp/benders/logger/UserFile.cpp index ba72d62f1..39baf0d6a 100644 --- a/src/cpp/benders/logger/UserFile.cpp +++ b/src/cpp/benders/logger/UserFile.cpp @@ -40,6 +40,21 @@ void UserFile::display_message(const std::string &str, _file.flush(); } +void UserFile::PrintIterationSeparatorBegin() { + _file << PrefixMessage(LogUtils::LOGLEVEL::INFO, CONTEXT); + std::string sep_msg("/*\\"); + sep_msg += std::string(74, '-'); + _file << sep_msg << std::endl; + _file.flush(); +} +void UserFile::PrintIterationSeparatorEnd() { + _file << PrefixMessage(LogUtils::LOGLEVEL::INFO, CONTEXT); + std::string sep_msg(74, '-'); + sep_msg = "\\*/" + sep_msg; + _file << sep_msg << std::endl; + _file.flush(); +} + void UserFile::log_at_initialization(const int it_number) { _file << PrefixMessage(LogUtils::LOGLEVEL::INFO, CONTEXT) << "ITERATION " << it_number << ":" << std::endl; diff --git a/src/cpp/benders/logger/include/logger/Master.h b/src/cpp/benders/logger/include/logger/Master.h index ff4b44aa7..2bac75237 100644 --- a/src/cpp/benders/logger/include/logger/Master.h +++ b/src/cpp/benders/logger/include/logger/Master.h @@ -28,6 +28,9 @@ class Master : public ILogger { void display_message(const std::string &str, LogUtils::LOGLEVEL level) override; + virtual void PrintIterationSeparatorBegin() override; + virtual void PrintIterationSeparatorEnd() override; + void log_at_initialization(const int it_number) override; void log_iteration_candidates(const LogData &d) override; diff --git a/src/cpp/benders/logger/include/logger/MathLogger.h b/src/cpp/benders/logger/include/logger/MathLogger.h index c9e0a16d8..c3a2b60f3 100644 --- a/src/cpp/benders/logger/include/logger/MathLogger.h +++ b/src/cpp/benders/logger/include/logger/MathLogger.h @@ -12,6 +12,8 @@ class MathLoggerFile : public MathLoggerImplementation { std::streamsize width = 30); void display_message(const std::string& msg) override; + virtual void PrintIterationSeparatorBegin() override; + virtual void PrintIterationSeparatorEnd() override; private: std::ofstream file_stream_; @@ -21,4 +23,7 @@ class MathLoggerOstream : public MathLoggerImplementation { explicit MathLoggerOstream(const BENDERSMETHOD& method, std::streamsize width = 20) : MathLoggerImplementation(method, width, HEADERSTYPE::SHORT) {} + + virtual void PrintIterationSeparatorBegin() override; + virtual void PrintIterationSeparatorEnd() override; }; diff --git a/src/cpp/benders/logger/include/logger/User.h b/src/cpp/benders/logger/include/logger/User.h index 721825682..a1ca2eca1 100644 --- a/src/cpp/benders/logger/include/logger/User.h +++ b/src/cpp/benders/logger/include/logger/User.h @@ -20,6 +20,9 @@ class User : public ILogger { void display_message(const std::string &str, LogUtils::LOGLEVEL level) override; + virtual void PrintIterationSeparatorBegin() override; + virtual void PrintIterationSeparatorEnd() override; + void log_at_initialization(const int it_number) override; void log_iteration_candidates(const LogData &d) override; diff --git a/src/cpp/benders/logger/include/logger/UserFile.h b/src/cpp/benders/logger/include/logger/UserFile.h index a0c272e5b..ca7c33ec8 100644 --- a/src/cpp/benders/logger/include/logger/UserFile.h +++ b/src/cpp/benders/logger/include/logger/UserFile.h @@ -20,6 +20,10 @@ class UserFile : public ILogger { void display_message(const std::string &str) override; void display_message(const std::string &str, LogUtils::LOGLEVEL level) override; + + virtual void PrintIterationSeparatorBegin() override; + virtual void PrintIterationSeparatorEnd() override; + void log_at_initialization(const int it_number) override; void log_iteration_candidates(const LogData &d) override; diff --git a/src/cpp/exe/CMakeLists.txt b/src/cpp/exe/CMakeLists.txt index efe61cbf1..85a7d6427 100644 --- a/src/cpp/exe/CMakeLists.txt +++ b/src/cpp/exe/CMakeLists.txt @@ -14,4 +14,5 @@ add_subdirectory ("${CMAKE_CURRENT_SOURCE_DIR}/full_run") add_subdirectory ("${CMAKE_CURRENT_SOURCE_DIR}/antares_archive_updater") add_subdirectory ("${CMAKE_CURRENT_SOURCE_DIR}/benders") +add_subdirectory ("${CMAKE_CURRENT_SOURCE_DIR}/ExtLoop") diff --git a/src/cpp/exe/ExtLoop/CMakeLists.txt b/src/cpp/exe/ExtLoop/CMakeLists.txt new file mode 100644 index 000000000..7a2dc6170 --- /dev/null +++ b/src/cpp/exe/ExtLoop/CMakeLists.txt @@ -0,0 +1,33 @@ +# =========================================================================== +# CMake configuration +# =========================================================================== + +# =========================================================================== +# Targets +# =========================================================================== + +# --------------------------------------------------------------------------- +# MPI Benders Exe +# --------------------------------------------------------------------------- + +add_executable (ext_loop + ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp +) + +find_package(MPI REQUIRED) +if(UNIX) +set(CMAKE_CXX_COMPILER ${MPI_CXX_COMPILER}) +endif() + +#IF (WIN32) +# target_link_libraries (benders +# ${PROJECT_NAME}::benders_sequential_core ${PROJECT_NAME}::benders_mpi_core +# msmpi libboost_serialization-vc141-mt-x64-1_67) +#ELSE (WIN32) + target_link_libraries (ext_loop + ${PROJECT_NAME}::benders_mpi_core + factories + ) +#ENDIF (WIN32) + +install(TARGETS ext_loop DESTINATION bin) diff --git a/src/cpp/exe/ExtLoop/main.cpp b/src/cpp/exe/ExtLoop/main.cpp new file mode 100644 index 000000000..08b804404 --- /dev/null +++ b/src/cpp/exe/ExtLoop/main.cpp @@ -0,0 +1,9 @@ +#include "BendersFactory.h" +#include "common_mpi.h" + +int main(int argc, char **argv) { + mpi::environment env(argc, argv); + mpi::communicator world; + auto benders_factory = BendersMainFactory(argc, argv, env, world); + return benders_factory.RunExternalLoop(); +} diff --git a/src/cpp/helpers/solver_utils.cc b/src/cpp/helpers/solver_utils.cc index ed2637028..a328c2da7 100644 --- a/src/cpp/helpers/solver_utils.cc +++ b/src/cpp/helpers/solver_utils.cc @@ -59,15 +59,17 @@ void solver_addrows(SolverAbstract &solver_p, std::vector const &qrtype_p, std::vector const &range_p, std::vector const &mstart_p, std::vector const &mclind_p, - std::vector const &dmatval_p) -{ - assert(qrtype_p.size() == rhs_p.size()); - assert((range_p.size() == 0) || (range_p.size() == qrtype_p.size())); - assert(mclind_p.size() == dmatval_p.size()); + std::vector const &dmatval_p, + std::vector const &names) { + assert(qrtype_p.size() == rhs_p.size()); + assert((range_p.size() == 0) || (range_p.size() == qrtype_p.size())); + assert(mclind_p.size() == dmatval_p.size()); - int nrows = rhs_p.size(); + int nrows = rhs_p.size(); - solver_p.add_rows(nrows, dmatval_p.size(), qrtype_p.data(), rhs_p.data(), range_p.data(), mstart_p.data(), mclind_p.data(), dmatval_p.data()); + solver_p.add_rows(nrows, dmatval_p.size(), qrtype_p.data(), rhs_p.data(), + range_p.data(), mstart_p.data(), mclind_p.data(), + dmatval_p.data(), names); } void solver_getlpsolution(SolverAbstract::Ptr const solver_p, std::vector &x_p) diff --git a/src/cpp/helpers/solver_utils.h b/src/cpp/helpers/solver_utils.h index abcdfb735..72e455a48 100644 --- a/src/cpp/helpers/solver_utils.h +++ b/src/cpp/helpers/solver_utils.h @@ -88,6 +88,8 @@ void solver_addcols(SolverAbstract &solver_p, std::vector const &objx_p, * for the elements in each row. * @param dmatval_p : Double array of containing the (contiguous) element * values. + * @param names : rows names + * values. * * @note ignores non-binding rows */ @@ -96,7 +98,8 @@ void solver_addrows(SolverAbstract &solver_p, std::vector const &qrtype_p, std::vector const &range_p, std::vector const &mstart_p, std::vector const &mclind_p, - std::vector const &dmatval_p); + std::vector const &dmatval_p, + std::vector const &names = {}); /** * @brief returns the solution of a solved problem diff --git a/src/cpp/lpnamer/helper/ColumnToChange.h b/src/cpp/lpnamer/helper/ColumnToChange.h index 6f91d6204..bb1880538 100644 --- a/src/cpp/lpnamer/helper/ColumnToChange.h +++ b/src/cpp/lpnamer/helper/ColumnToChange.h @@ -4,7 +4,8 @@ #include using colId = unsigned int; struct ColumnToChange { - ColumnToChange(colId id, int time_step) : id(id), time_step(time_step){}; + ColumnToChange(colId id, unsigned int time_step) + : id(id), time_step(time_step){}; bool operator==(const ColumnToChange& other) const; colId id; diff --git a/src/cpp/lpnamer/input_reader/VariableFileReader.cpp b/src/cpp/lpnamer/input_reader/VariableFileReader.cpp index 2201c31fa..24dbeb44c 100644 --- a/src/cpp/lpnamer/input_reader/VariableFileReader.cpp +++ b/src/cpp/lpnamer/input_reader/VariableFileReader.cpp @@ -5,7 +5,7 @@ #include void updateMapColumn(const std::vector& links, int link_id, - colId id, int time_step, + colId id, unsigned int time_step, std::map& mapColumn) { auto it = std::find_if(links.begin(), links.end(), [link_id](const ActiveLink& link) { @@ -59,7 +59,7 @@ void VariableFileReader::ReadVarsFromStream( if (variable == variable_name_config.ntc_variable_name) { int pays; int link_id; - int time_step; + unsigned int time_step; buffer >> pays; buffer >> link_id; buffer >> time_step; diff --git a/src/cpp/lpnamer/input_reader/VariableFileReader.h b/src/cpp/lpnamer/input_reader/VariableFileReader.h index 8a2c1d8dc..0284018d3 100644 --- a/src/cpp/lpnamer/input_reader/VariableFileReader.h +++ b/src/cpp/lpnamer/input_reader/VariableFileReader.h @@ -14,7 +14,7 @@ #include "LogUtils.h" void updateMapColumn(const std::vector& links, int link_id, - colId id, int time_step, + colId id, unsigned int time_step, std::map& mapColumn); struct VariableFileReadNameConfiguration { diff --git a/src/cpp/lpnamer/model/Problem.h b/src/cpp/lpnamer/model/Problem.h index ab44c9657..c48e7bc3c 100644 --- a/src/cpp/lpnamer/model/Problem.h +++ b/src/cpp/lpnamer/model/Problem.h @@ -62,6 +62,11 @@ class Problem : public SolverAbstract { void get_obj(double *obj, int first, int last) const override { solver_abstract_->get_obj(obj, first, last); } + + void set_obj_to_zero() override { solver_abstract_->set_obj_to_zero(); } + void set_obj(const double *obj, int first, int last) override { + solver_abstract_->set_obj(obj, first, last); + } void get_rows(int *mstart, int *mclind, double *dmatval, int size, int *nels, int first, int last) const override { solver_abstract_->get_rows(mstart, mclind, dmatval, size, nels, first, @@ -108,9 +113,10 @@ class Problem : public SolverAbstract { } void add_rows(int newrows, int newnz, const char *qrtype, const double *rhs, const double *range, const int *mstart, const int *mclind, - const double *dmatval) override { + const double *dmatval, + const std::vector &names = {}) override { solver_abstract_->add_rows(newrows, newnz, qrtype, rhs, range, mstart, - mclind, dmatval); + mclind, dmatval, names); } void add_cols(int newcol, int newnz, const double *objx, const int *mstart, const int *mrwind, const double *dmatval, const double *bdl, @@ -121,6 +127,10 @@ class Problem : public SolverAbstract { void add_name(int type, const char *cnames, int indice) override { solver_abstract_->add_name(type, cnames, indice); } + void add_names(int type, const std::vector &cnames, int first, + int end) override { + solver_abstract_->add_names(type, cnames, first, end); + } void chg_obj(const std::vector &mindex, const std::vector &obj) override { solver_abstract_->chg_obj(mindex, obj); diff --git a/src/cpp/multisolver_interface/SolverCbc.cpp b/src/cpp/multisolver_interface/SolverCbc.cpp index 058968170..06a9480e4 100644 --- a/src/cpp/multisolver_interface/SolverCbc.cpp +++ b/src/cpp/multisolver_interface/SolverCbc.cpp @@ -238,6 +238,22 @@ void SolverCbc::get_obj(double *obj, int first, int last) const { } } +void SolverCbc::set_obj_to_zero() { + auto ncols = get_ncols(); + std::vector zeros_val(ncols, 0.0); + _clp_inner_solver.setObjective(zeros_val.data()); +} + +void SolverCbc::set_obj(const double *obj, int first, int last) { + if (last - first + 1 == get_ncols()) { + _clp_inner_solver.setObjective(obj); + } else { + for (int index = first; index < last + 1; ++index) { + _clp_inner_solver.setObjCoeff(index, obj[index]); + } + } +} + void SolverCbc::get_rows(int *mstart, int *mclind, double *dmatval, int size, int *nels, int first, int last) const { CoinPackedMatrix matrix = *_clp_inner_solver.getMatrixByRow(); @@ -302,6 +318,7 @@ void SolverCbc::get_ub(double *ub, int first, int last) const { } } +// TODO update see SolverCbc::get_col_index int SolverCbc::get_row_index(std::string const &name) { int id = 0; int nrows = get_nrows(); @@ -380,13 +397,22 @@ void SolverCbc::del_rows(int first, int last) { void SolverCbc::add_rows(int newrows, int newnz, const char *qrtype, const double *rhs, const double *range, const int *mstart, const int *mclind, - const double *dmatval) { + const double *dmatval, + const std::vector &row_names) { std::vector rowLower(newrows); std::vector rowUpper(newrows); + int nrowInit = get_nrows(); + coin_common::fill_row_bounds_from_new_rows_data(rowLower, rowUpper, newrows, qrtype, rhs); _clp_inner_solver.addRows(newrows, mstart, mclind, dmatval, rowLower.data(), rowUpper.data()); + if (row_names.size() > 0) { + int nrowFinal = get_nrows(); + for (int i = nrowInit; i < nrowFinal; i++) { + chg_row_name(i, row_names[i - nrowInit]); + } + } } void SolverCbc::add_cols(int newcol, int newnz, const double *objx, @@ -409,6 +435,14 @@ void SolverCbc::add_name(int type, const char *cnames, int indice) { throw NotImplementedFeatureSolverException(error); } +void SolverCbc::add_names(int type, const std::vector &cnames, + int first, int end) { + // TODO + auto error = + LOGLOCATION + "ERROR : addnames not implemented in the CLP interface."; + throw NotImplementedFeatureSolverException(error); +} + void SolverCbc::chg_obj(const std::vector &mindex, const std::vector &obj) { assert(obj.size() == mindex.size()); diff --git a/src/cpp/multisolver_interface/SolverCbc.h b/src/cpp/multisolver_interface/SolverCbc.h index f81228f0d..48e161981 100644 --- a/src/cpp/multisolver_interface/SolverCbc.h +++ b/src/cpp/multisolver_interface/SolverCbc.h @@ -101,6 +101,8 @@ class SolverCbc : public SolverAbstract { virtual int get_nelems() const override; virtual int get_n_integer_vars() const override; virtual void get_obj(double *obj, int first, int last) const override; + void set_obj_to_zero() override; + void set_obj(const double *obj, int first, int last) override; virtual void get_rows(int *mstart, int *mclind, double *dmatval, int size, int *nels, int first, int last) const override; virtual void get_row_type(char *qrtype, int first, int last) const override; @@ -126,12 +128,15 @@ class SolverCbc : public SolverAbstract { virtual void add_rows(int newrows, int newnz, const char *qrtype, const double *rhs, const double *range, const int *mstart, const int *mclind, - const double *dmatval) override; + const double *dmatval, + const std::vector &names = {}) override; virtual void add_cols(int newcol, int newnz, const double *objx, const int *mstart, const int *mrwind, const double *dmatval, const double *bdl, const double *bdu) override; virtual void add_name(int type, const char *cnames, int indice) override; + virtual void add_names(int type, const std::vector &cnames, + int first, int end) override; virtual void chg_obj(const std::vector &mindex, const std::vector &obj) override; virtual void chg_obj_direction(const bool minimize) override; diff --git a/src/cpp/multisolver_interface/SolverClp.cpp b/src/cpp/multisolver_interface/SolverClp.cpp index 8360103a7..761f9e180 100644 --- a/src/cpp/multisolver_interface/SolverClp.cpp +++ b/src/cpp/multisolver_interface/SolverClp.cpp @@ -129,6 +129,22 @@ void SolverClp::get_obj(double *obj, int first, int last) const { } } +void SolverClp::set_obj_to_zero() { + auto ncols = get_ncols(); + std::vector zeros_val(ncols, 0.0); + _clp.setRowObjective(zeros_val.data()); +} + +void SolverClp::set_obj(const double *obj, int first, int last) { + if (last - first + 1 == get_ncols()) { + _clp.setRowObjective(obj); + } else { + for (int index = first; index < last + 1; ++index) { + _clp.setObjCoeff(index, obj[index]); + } + } +} + void SolverClp::get_rows(int *mstart, int *mclind, double *dmatval, int size, int *nels, int first, int last) const { CoinPackedMatrix matrix = *_clp.matrix(); @@ -256,14 +272,22 @@ void SolverClp::del_rows(int first, int last) { void SolverClp::add_rows(int newrows, int newnz, const char *qrtype, const double *rhs, const double *range, const int *mstart, const int *mclind, - const double *dmatval) { + const double *dmatval, + const std::vector &row_names) { std::vector rowLower(newrows); std::vector rowUpper(newrows); + int nrowInit = get_nrows(); coin_common::fill_row_bounds_from_new_rows_data(rowLower, rowUpper, newrows, qrtype, rhs); _clp.addRows(newrows, rowLower.data(), rowUpper.data(), mstart, mclind, dmatval); + if (row_names.size() > 0) { + int nrowFinal = get_nrows(); + for (int i = nrowInit; i < nrowFinal; i++) { + chg_row_name(i, row_names[i - nrowInit]); + } + } } void SolverClp::add_cols(int newcol, int newnz, const double *objx, @@ -284,6 +308,13 @@ void SolverClp::add_name(int type, const char *cnames, int indice) { LOGLOCATION + "ERROR : addnames not implemented in the CLP interface."; throw NotImplementedFeatureSolverException(error); } +void SolverClp::add_names(int type, const std::vector &cnames, + int first, int end) { + // TODO + auto error = + LOGLOCATION + "ERROR : addnames not implemented in the CLP interface."; + throw NotImplementedFeatureSolverException(error); +} void SolverClp::chg_obj(const std::vector &mindex, const std::vector &obj) { diff --git a/src/cpp/multisolver_interface/SolverClp.h b/src/cpp/multisolver_interface/SolverClp.h index 9be4aa893..766e4c510 100644 --- a/src/cpp/multisolver_interface/SolverClp.h +++ b/src/cpp/multisolver_interface/SolverClp.h @@ -98,6 +98,8 @@ class SolverClp : public SolverAbstract { virtual int get_nelems() const override; virtual int get_n_integer_vars() const override; virtual void get_obj(double *obj, int first, int last) const override; + void set_obj_to_zero() override; + void set_obj(const double *obj, int first, int last) override; virtual void get_rows(int *mstart, int *mclind, double *dmatval, int size, int *nels, int first, int last) const override; virtual void get_row_type(char *qrtype, int first, int last) const override; @@ -123,12 +125,15 @@ class SolverClp : public SolverAbstract { virtual void add_rows(int newrows, int newnz, const char *qrtype, const double *rhs, const double *range, const int *mstart, const int *mclind, - const double *dmatval) override; + const double *dmatval, + const std::vector &names = {}) override; virtual void add_cols(int newcol, int newnz, const double *objx, const int *mstart, const int *mrwind, const double *dmatval, const double *bdl, const double *bdu) override; virtual void add_name(int type, const char *cnames, int indice) override; + virtual void add_names(int type, const std::vector &cnames, + int first, int end) override; virtual void chg_obj(const std::vector &mindex, const std::vector &obj) override; virtual void chg_obj_direction(const bool minimize) override; diff --git a/src/cpp/multisolver_interface/SolverXpress.cpp b/src/cpp/multisolver_interface/SolverXpress.cpp index 6412d5de3..cd0e4cf57 100644 --- a/src/cpp/multisolver_interface/SolverXpress.cpp +++ b/src/cpp/multisolver_interface/SolverXpress.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "StringManip.h" @@ -220,6 +221,19 @@ void SolverXpress::get_obj(double *obj, int first, int last) const { zero_status_check(status, "get objective function", LOGLOCATION); } +void SolverXpress::set_obj_to_zero() { + auto ncols = get_ncols(); + std::vector zeros_val(ncols, 0.0); + set_obj(zeros_val.data(), 0, ncols); +} + +void SolverXpress::set_obj(const double *obj, int first, int last) { + auto ncols = last - first + 1; + std::vector col_ind(ncols); + std::iota(col_ind.begin(), col_ind.end(), first); + int status = XPRSchgobj(_xprs, ncols, col_ind.data(), obj); + zero_status_check(status, "set objective function", LOGLOCATION); +} void SolverXpress::get_rows(int *mstart, int *mclind, double *dmatval, int size, int *nels, int first, int last) const { int status = @@ -339,10 +353,16 @@ void SolverXpress::del_rows(int first, int last) { void SolverXpress::add_rows(int newrows, int newnz, const char *qrtype, const double *rhs, const double *range, const int *mstart, const int *mclind, - const double *dmatval) { + const double *dmatval, + const std::vector &row_names) { + int nrowInit = get_nrows(); int status = XPRSaddrows(_xprs, newrows, newnz, qrtype, rhs, range, mstart, mclind, dmatval); zero_status_check(status, "add rows", LOGLOCATION); + if (row_names.size() > 0) { + int nrowFinal = get_nrows(); + add_names(1, row_names, nrowInit, nrowFinal - 1); + } } void SolverXpress::add_cols(int newcol, int newnz, const double *objx, @@ -359,6 +379,17 @@ void SolverXpress::add_name(int type, const char *cnames, int indice) { zero_status_check(status, "add names", LOGLOCATION); } +void SolverXpress::add_names(int type, const std::vector &cnames, + int first, int end) { + std::vector row_names_charp; + for (auto name : cnames) { + name += '\0'; + row_names_charp.insert(row_names_charp.end(), name.begin(), name.end()); + } + int status = XPRSaddnames(_xprs, type, row_names_charp.data(), first, end); + zero_status_check(status, "add names", LOGLOCATION); +} + void SolverXpress::chg_obj(const std::vector &mindex, const std::vector &obj) { assert(obj.size() == mindex.size()); diff --git a/src/cpp/multisolver_interface/SolverXpress.h b/src/cpp/multisolver_interface/SolverXpress.h index 432936690..0c6226057 100644 --- a/src/cpp/multisolver_interface/SolverXpress.h +++ b/src/cpp/multisolver_interface/SolverXpress.h @@ -95,6 +95,8 @@ class SolverXpress : public SolverAbstract { virtual int get_nelems() const override; virtual int get_n_integer_vars() const override; virtual void get_obj(double *obj, int first, int last) const override; + void set_obj_to_zero() override; + void set_obj(const double *obj, int first, int last) override; virtual void get_rows(int *mstart, int *mclind, double *dmatval, int size, int *nels, int first, int last) const override; virtual void get_row_type(char *qrtype, int first, int last) const override; @@ -120,12 +122,15 @@ class SolverXpress : public SolverAbstract { virtual void add_rows(int newrows, int newnz, const char *qrtype, const double *rhs, const double *range, const int *mstart, const int *mclind, - const double *dmatval) override; + const double *dmatval, + const std::vector &names = {}) override; virtual void add_cols(int newcol, int newnz, const double *objx, const int *mstart, const int *mrwind, const double *dmatval, const double *bdl, const double *bdu) override; virtual void add_name(int type, const char *cnames, int indice) override; + virtual void add_names(int type, const std::vector &cnames, + int first, int end) override; virtual void chg_obj(const std::vector &mindex, const std::vector &obj) override; virtual void chg_obj_direction(const bool minimize) override; diff --git a/src/cpp/multisolver_interface/include/multisolver_interface/SolverAbstract.h b/src/cpp/multisolver_interface/include/multisolver_interface/SolverAbstract.h index 6ad34e3be..74a46b068 100644 --- a/src/cpp/multisolver_interface/include/multisolver_interface/SolverAbstract.h +++ b/src/cpp/multisolver_interface/include/multisolver_interface/SolverAbstract.h @@ -353,6 +353,17 @@ class SolverAbstract { */ virtual void get_obj(double *obj, int first, int last) const = 0; + /** + * @brief Set the objective function coefficients to zero + */ + virtual void set_obj_to_zero() = 0; + + /** + * @brief Set the objective function coefficients for the columns in a + * given range + */ + virtual void set_obj(const double *obj, int first, int last) = 0; + /** * @brief get coefficients of rows from index first to last * @@ -514,7 +525,8 @@ class SolverAbstract { virtual void add_rows(int newrows, int newnz, const char *qrtype, const double *rhs, const double *range, const int *mstart, const int *mclind, - const double *dmatval) = 0; + const double *dmatval, + const std::vector &names = {}) = 0; /** * @brief Adds new columns to the problem @@ -548,6 +560,8 @@ class SolverAbstract { * @param indice : index of the row or of the column. */ virtual void add_name(int type, const char *cnames, int indice) = 0; + virtual void add_names(int type, const std::vector &cnames, + int first, int end) = 0; /** * @brief Change coefficients in objective function diff --git a/src/cpp/xpansion_interfaces/ILogger.h b/src/cpp/xpansion_interfaces/ILogger.h index acf1bbaa4..f9fbdfd58 100644 --- a/src/cpp/xpansion_interfaces/ILogger.h +++ b/src/cpp/xpansion_interfaces/ILogger.h @@ -64,8 +64,12 @@ struct LogData { double subproblem_time; int cumulative_number_of_subproblem_resolved; }; + struct ILoggerBenders { virtual void display_message(const std::string &str) = 0; + virtual void PrintIterationSeparatorBegin() = 0; + virtual void PrintIterationSeparatorEnd() = 0; + virtual ~ILoggerBenders() = default; }; struct BendersLoggerBase : public ILoggerBenders { @@ -78,6 +82,18 @@ struct BendersLoggerBase : public ILoggerBenders { loggers.push_back(logger); } + virtual void PrintIterationSeparatorBegin() override { + for (auto logger : loggers) { + logger->PrintIterationSeparatorBegin(); + } + } + + virtual void PrintIterationSeparatorEnd() override { + for (auto logger : loggers) { + logger->PrintIterationSeparatorEnd(); + } + } + private: std::vector> loggers; }; @@ -88,6 +104,8 @@ class ILogger : public ILoggerBenders { virtual void display_message(const std::string &str) = 0; virtual void display_message(const std::string &str, LogUtils::LOGLEVEL level) = 0; + virtual void PrintIterationSeparatorBegin() = 0; + virtual void PrintIterationSeparatorEnd() = 0; virtual void log_at_initialization(const int it_number) = 0; virtual void log_iteration_candidates(const LogData &d) = 0; virtual void log_master_solving_duration(double durationInSeconds) = 0; diff --git a/tests/cpp/CMakeLists.txt b/tests/cpp/CMakeLists.txt index f439763dd..579066af5 100644 --- a/tests/cpp/CMakeLists.txt +++ b/tests/cpp/CMakeLists.txt @@ -31,3 +31,4 @@ add_subdirectory(restart_benders) add_subdirectory(zip_mps) add_subdirectory(benders) add_subdirectory(full_run) +add_subdirectory(ext_loop) diff --git a/tests/cpp/TestDoubles/LoggerStub.h b/tests/cpp/TestDoubles/LoggerStub.h index a0a965c31..99f91701a 100644 --- a/tests/cpp/TestDoubles/LoggerStub.h +++ b/tests/cpp/TestDoubles/LoggerStub.h @@ -11,6 +11,12 @@ class LoggerNOOPStub : public ILogger { void display_message(const std::string& str) override {} void display_message(const std::string& str, LogUtils::LOGLEVEL level) override {} + void PrintIterationSeparatorBegin() override { + // + } + void PrintIterationSeparatorEnd() override { + // + } void log_at_initialization(const int it_number) override {} void log_iteration_candidates(const LogData& d) override {} void log_master_solving_duration(double durationInSeconds) override {} diff --git a/tests/cpp/ext_loop/CMakeLists.txt b/tests/cpp/ext_loop/CMakeLists.txt new file mode 100644 index 000000000..4121ae77e --- /dev/null +++ b/tests/cpp/ext_loop/CMakeLists.txt @@ -0,0 +1,21 @@ +add_executable(ext_loop_test + ext_loop_test.cpp) + +target_link_libraries(ext_loop_test + PRIVATE + solvers + external_loop + benders_by_batch_core + benders_core + output_core + logger_lib + GTest::Main + tests_utils) + +# target_include_directories(ext_loop_test +# PRIVATE +# ${CMAKE_CURRENT_SOURCE_DIR}/../TestDoubles/ +# ) + +add_test(NAME unit_ext_loop COMMAND ext_loop_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) +set_property(TEST unit_ext_loop PROPERTY LABELS unit) \ No newline at end of file diff --git a/tests/cpp/ext_loop/ext_loop_test.cpp b/tests/cpp/ext_loop/ext_loop_test.cpp new file mode 100644 index 000000000..4b68c7f1f --- /dev/null +++ b/tests/cpp/ext_loop/ext_loop_test.cpp @@ -0,0 +1,250 @@ + +// #include "MasterUpdate.h" +// #include "ext_loop_test.h" + +#include "LoggerFactories.h" +#include "MasterUpdate.h" +#include "OuterLoopCriterion.h" +#include "WriterFactories.h" +#include "gtest/gtest.h" +#include "multisolver_interface/environment.h" + +int my_argc; +char** my_argv; + +boost::mpi::environment* penv = nullptr; +boost::mpi::communicator* pworld = nullptr; +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + mpi::environment env(my_argc, my_argv); + mpi::communicator world; + penv = &env; + pworld = &world; + my_argc = argc; + my_argv = argv; + return RUN_ALL_TESTS(); +} + +//-------------------- OuterLoopCriterionTest ------------------------- + +class OuterLoopCriterionTest : public ::testing::Test {}; + +TEST_F(OuterLoopCriterionTest, IsCriterionHigh) { + double threshold = 1.4; + double epsilon = 1e-1; + double max_unsup_energy = 0.1; + const ExternalLoopOptions options = {threshold, epsilon, max_unsup_energy}; + PlainData::Variables variables = { + {"PositiveUnsuppliedEnergy::1", "PositiveUnsuppliedEnergy::2", "var3"}, + {0.2, 0.3, 68}}; + double criterion_value = 2.0; // two vars named ^PositiveUnsuppliedEnergy + // with value > max_unsup_energy + + PlainData::SubProblemData subProblemData; + subProblemData.variables = variables; + SubProblemDataMap cut_trace = { + std::make_pair(std::string("P1"), subProblemData)}; + + WorkerMasterData worker_master_data; + worker_master_data._cut_trace = cut_trace; + + OuterloopCriterionLossOfLoad criterion(options); + + EXPECT_EQ(criterion.IsCriterionSatisfied(worker_master_data), + CRITERION::HIGH); + EXPECT_EQ(criterion.CriterionValue(), criterion_value); +} + +TEST_F(OuterLoopCriterionTest, IsCriterionLow) { + double threshold = 4; + double epsilon = 1e-1; + double max_unsup_energy = 0.1; + const ExternalLoopOptions options = {threshold, epsilon, max_unsup_energy}; + PlainData::Variables variables = { + {"PositiveUnsuppliedEnergy::1", "PositiveUnsuppliedEnergy::2", "var3"}, + {0.2, 0.3, 68}}; + double criterion_value = 2.0; // two vars named PositiveUnsuppliedEnergy with + // value > max_unsup_energy + + PlainData::SubProblemData subProblemData; + subProblemData.variables = variables; + SubProblemDataMap cut_trace = { + std::make_pair(std::string("P1"), subProblemData)}; + + WorkerMasterData worker_master_data; + worker_master_data._cut_trace = cut_trace; + + OuterloopCriterionLossOfLoad criterion(options); + + EXPECT_EQ(criterion.IsCriterionSatisfied(worker_master_data), CRITERION::LOW); + EXPECT_EQ(criterion.CriterionValue(), criterion_value); +} + +TEST_F(OuterLoopCriterionTest, IsMet) { + double threshold = 2.0; + double epsilon = 1e-1; + double max_unsup_energy = 0.1; + const ExternalLoopOptions options = {threshold, epsilon, max_unsup_energy}; + PlainData::Variables variables = { + {"PositiveUnsuppliedEnergy::1", "PositiveUnsuppliedEnergy::2", "var3"}, + {0.2, 0.3, 68}}; + double criterion_value = 2.0; // two vars named PositiveUnsuppliedEnergy with + // value > max_unsup_energy + + PlainData::SubProblemData subProblemData; + subProblemData.variables = variables; + SubProblemDataMap cut_trace = { + std::make_pair(std::string("P1"), subProblemData)}; + + WorkerMasterData worker_master_data; + worker_master_data._cut_trace = cut_trace; + + OuterloopCriterionLossOfLoad criterion(options); + + EXPECT_EQ(criterion.IsCriterionSatisfied(worker_master_data), + CRITERION::IS_MET); + EXPECT_EQ(criterion.CriterionValue(), criterion_value); +} + +//-------------------- MasterUpdateBaseTest ------------------------- +const auto STUDY_PATH = + std::filesystem::path("data_test") / "external_loop_test"; +const auto OPTIONS_FILE = STUDY_PATH / "lp" / "options.json"; + +class MasterUpdateBaseTest : public ::testing::TestWithParam { + public: + pBendersBase benders; + std::shared_ptr math_log_driver; + Logger logger; + Writer writer; + + void SetUp() override { + math_log_driver = MathLoggerFactory::get_void_logger(); + logger = build_void_logger(); + writer = build_void_writer(); + } + BendersBaseOptions BuildBendersOptions() { + SimulationOptions options(OPTIONS_FILE); + return options.get_benders_options(); + } +}; + +auto solvers() { + std::vector solvers_name; + solvers_name.push_back("COIN"); + if (LoadXpress::XpressIsCorrectlyInstalled()) { + solvers_name.push_back("XPRESS"); + } + return solvers_name; +} + +INSTANTIATE_TEST_SUITE_P(availsolvers, MasterUpdateBaseTest, + ::testing::ValuesIn(solvers())); +double LambdaMax(pBendersBase benders) { + const auto& obj = benders->MasterObjectiveFunctionCoeffs(); + const auto max_invest = benders->BestIterationWorkerMaster().get_max_invest(); + double lambda_max = 0; + for (const auto& [var_name, var_id] : benders->MasterVariables()) { + lambda_max += obj[var_id] * max_invest.at(var_name); + } + return lambda_max; +} + +void CheckMinInvestmentConstraint(const VariableMap& master_variables, + const std::vector& expected_coeffs, + const double expected_rhs, char expected_sign, + const std::vector& coeffs, + const double rhs, char sign) { + for (auto const& [name, var_id] : master_variables) { + ASSERT_EQ(expected_coeffs[var_id], coeffs[var_id]); + } + + ASSERT_EQ(expected_rhs, rhs); + ASSERT_EQ(expected_sign, sign); +} + +TEST_P(MasterUpdateBaseTest, ConstraintIsAddedBendersMPI) { + BendersBaseOptions benders_options = BuildBendersOptions(); + CouplingMap coupling_map = build_input(benders_options.STRUCTURE_FILE); + // override solver + benders_options.SOLVER_NAME = GetParam(); + benders = std::make_shared(benders_options, logger, writer, *penv, + *pworld, math_log_driver); + benders->set_input_map(coupling_map); + benders->DoFreeProblems(false); + benders->InitializeProblems(); + benders->launch(); + + MasterUpdateBase master_updater(benders, 0.5); + // update lambda_max + master_updater.Init(); + benders->ResetData(3.0); + benders->launch(); + auto num_constraints_master_before = benders->MasterGetnrows(); + master_updater.Update(CRITERION::LOW); + auto num_constraints_master_after = benders->MasterGetnrows(); + + auto master_variables = benders->MasterVariables(); + auto expected_coeffs = benders->MasterObjectiveFunctionCoeffs(); + + // criterion is low <=> lambda_max = min(lambda_max, invest_cost) + auto lambda_max = (std::min)(LambdaMax(benders), + benders->GetBestIterationData().invest_cost); + auto expected_rhs = 0.5 * lambda_max; + + // + ASSERT_EQ(num_constraints_master_after, num_constraints_master_before + 1); + + std::vector mstart(1 + 1); + auto n_elems = benders->MasterGetNElems(); + auto nnz = master_variables.size(); + std::vector mclind(n_elems); + std::vector matval(n_elems); + std::vector p_nels(1, 0); + + auto added_row_index = num_constraints_master_after - 1; + benders->MasterRowsCoeffs(mstart, mclind, matval, n_elems, p_nels, + added_row_index, added_row_index); + std::vector coeffs(benders->MasterGetncols()); + + for (auto ind = mstart[0]; ind < mstart[1]; ++ind) { + coeffs[mclind[ind]] = matval[ind]; + } + double rhs; + benders->MasterGetRhs(rhs, added_row_index); + std::vector qrtype(1); + benders->MasterGetRowType(qrtype, added_row_index, added_row_index); + CheckMinInvestmentConstraint(master_variables, expected_coeffs, expected_rhs, + 'G', coeffs, rhs, qrtype[0]); + benders->free(); +} + +TEST_P(MasterUpdateBaseTest, InitialRhs) { + BendersBaseOptions benders_options = BuildBendersOptions(); + benders_options.SOLVER_NAME = GetParam(); + CouplingMap coupling_map = build_input(benders_options.STRUCTURE_FILE); + + benders = std::make_shared(benders_options, logger, writer, *penv, + *pworld, math_log_driver); + benders->set_input_map(coupling_map); + benders->DoFreeProblems(false); + benders->InitializeProblems(); + + benders->launch(); + + MasterUpdateBase master_updater(benders, 0.5); + // update lambda_max + master_updater.Init(); + auto lambda_max = LambdaMax(benders); + benders->ResetData(3.0); + benders->launch(); + master_updater.Update(CRITERION::HIGH); + auto expected_initial_rhs = lambda_max * 0.5; + + auto added_row_index = benders->MasterGetnrows() - 1; + double rhs; + benders->MasterGetRhs(rhs, added_row_index); + EXPECT_EQ(expected_initial_rhs, rhs); + benders->free(); +} \ No newline at end of file diff --git a/tests/cpp/logger/logger_test.cpp b/tests/cpp/logger/logger_test.cpp index c97b7358a..2fcfc1a24 100644 --- a/tests/cpp/logger/logger_test.cpp +++ b/tests/cpp/logger/logger_test.cpp @@ -571,6 +571,13 @@ class SimpleLoggerMock : public ILogger { _displaymessage = str; } + void PrintIterationSeparatorBegin() override { + // + } + void PrintIterationSeparatorEnd() override { + // + } + void log_at_initialization(const int it_number) override { _initCall = true; } void log_iteration_candidates(const LogData& d) override { @@ -812,7 +819,7 @@ TEST(MathLoggerHeadersManagerTest, LongBenders) { "SPWallTime (s)", "SPCpuTime (s)", "NotSolvingWallTime (s)"}; - ASSERT_EQ(expected_headers, headers_manager.headers_list); + ASSERT_EQ(expected_headers, headers_manager.HeadersList()); } TEST(MathLoggerHeadersManagerTest, ShortBenders) { @@ -822,12 +829,12 @@ TEST(MathLoggerHeadersManagerTest, ShortBenders) { std::vector expected_headers = { "Ite", "Lb", "Ub", "BestUb", "AbsGap", "RelGap", "MinSpx", "MaxSpx", "IteTime (s)", "MasterTime (s)", "SPWallTime (s)"}; - ASSERT_EQ(expected_headers, headers_manager.headers_list); + ASSERT_EQ(expected_headers, headers_manager.HeadersList()); } TEST(MathLoggerHeadersManagerTest, LongBendersByBatch) { HEADERSTYPE headers_type = HEADERSTYPE::LONG; - HeadersManager headers_manager(headers_type, BENDERSMETHOD::BENDERSBYBATCH); + HeadersManager headers_manager(headers_type, BENDERSMETHOD::BENDERS_BY_BATCH); std::vector expected_headers = {"Ite", "Lb", @@ -840,26 +847,26 @@ TEST(MathLoggerHeadersManagerTest, LongBendersByBatch) { "SPWallTime (s)", "SPCpuTime (s)", "NotSolvingWallTime (s)"}; - ASSERT_EQ(expected_headers, headers_manager.headers_list); + ASSERT_EQ(expected_headers, headers_manager.HeadersList()); } TEST(MathLoggerHeadersManagerTest, ShortBendersByBatch) { HEADERSTYPE headers_type = HEADERSTYPE::SHORT; - HeadersManager headers_manager(headers_type, BENDERSMETHOD::BENDERSBYBATCH); + HeadersManager headers_manager(headers_type, BENDERSMETHOD::BENDERS_BY_BATCH); std::vector expected_headers = { "Ite", "Lb", "MinSpx", "MaxSpx", "NbSubPbSolv", "IteTime (s)", "MasterTime (s)", "SPWallTime (s)"}; - ASSERT_EQ(expected_headers, headers_manager.headers_list); + ASSERT_EQ(expected_headers, headers_manager.HeadersList()); } TEST(MathLoggerBendersByBatchTest, HeadersListStdOutShort) { HEADERSTYPE headers_type = HEADERSTYPE::SHORT; - HeadersManager headers_manager(headers_type, BENDERSMETHOD::BENDERSBYBATCH); + HeadersManager headers_manager(headers_type, BENDERSMETHOD::BENDERS_BY_BATCH); std::streamsize width = 25; std::ostringstream expected_msg; - for (const auto& header : headers_manager.headers_list) { + for (const auto& header : headers_manager.HeadersList()) { expected_msg << std::setw(width) << std::left << header; } expected_msg << std::endl; @@ -874,11 +881,11 @@ TEST(MathLoggerBendersByBatchTest, HeadersListStdOutShort) { TEST(MathLoggerBendersByBatchTest, HeadersListFileLong) { HEADERSTYPE headers_type = HEADERSTYPE::LONG; - HeadersManager headers_manager(headers_type, BENDERSMETHOD::BENDERSBYBATCH); + HeadersManager headers_manager(headers_type, BENDERSMETHOD::BENDERS_BY_BATCH); std::streamsize width = 25; std::ostringstream expected_msg; - for (const auto& header : headers_manager.headers_list) { + for (const auto& header : headers_manager.HeadersList()) { expected_msg << std::setw(width) << std::left << header; } expected_msg << std::endl; diff --git a/tests/cpp/lp_namer/NOOPSolver.h b/tests/cpp/lp_namer/NOOPSolver.h index 7e4f2ce3e..047bba51e 100644 --- a/tests/cpp/lp_namer/NOOPSolver.h +++ b/tests/cpp/lp_namer/NOOPSolver.h @@ -22,6 +22,8 @@ class NOOPSolver: public SolverAbstract { virtual int get_nelems() const override { return 0; } virtual int get_n_integer_vars() const override { return 0; } virtual void get_obj(double *obj, int first, int last) const override {} + void set_obj_to_zero() override {} + void set_obj(const double *obj, int first, int last) override {} virtual void get_rows(int *mstart, int *mclind, double *dmatval, int size, int *nels, int first, int last) const override {} virtual void get_row_type(char *qrtype, int first, int last) const override {} @@ -51,12 +53,15 @@ class NOOPSolver: public SolverAbstract { virtual void add_rows(int newrows, int newnz, const char *qrtype, const double *rhs, const double *range, const int *mstart, const int *mclind, - const double *dmatval) override {} + const double *dmatval, + const std::vector &names = {}) override {} virtual void add_cols(int newcol, int newnz, const double *objx, const int *mstart, const int *mrwind, const double *dmatval, const double *bdl, const double *bdu) override {} virtual void add_name(int type, const char *cnames, int indice) override {} + virtual void add_names(int type, const std::vector &cnames, + int first, int end) override {} virtual void chg_obj(const std::vector &mindex, const std::vector &obj) override {} virtual void chg_obj_direction(const bool minimize) override {} From f121d428d6b53b63aab29c510d36a3ff4d3d0b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jason=20Mar=C3=A9chal?= <45510813+JasonMarechal25@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:27:35 +0100 Subject: [PATCH 2/9] Add high level design information (#757) --- conception/README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 conception/README.md diff --git a/conception/README.md b/conception/README.md new file mode 100644 index 000000000..f1cdde384 --- /dev/null +++ b/conception/README.md @@ -0,0 +1,44 @@ +# Xpansion design informations + +## Domains + +Xpansion aims at performing investment simulations for Antares studies. This means that the core domain of xpansion +could +be described as : **investment simulations for Antares studies** +This core domain is composed of subdomains: + +- Investment strategy: how to describe the investment strategy based on an Antares Simulation +- Linear optimization problem-solving + +## Mapping design with domains + +| Domain | Component | Details | +|--------------------------------------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Investment simulations for Antares studies | Xpansion as a whole | | +| Investment strategy | Problem Generation | Investment strategy is defined by candidates. Those candidates are used to enrich an Antares study simulation. It produces a linear optimization problem | +| Linear optimization problem-solv ing | Benders | Benders solve optimization problems. It is agnostic of the "business" of the core domain. Mathematical outputs need to be translated as business values in other components | + +## Conception folder + +In the conception folder you will find design documentation. + +### Architecture decision records + +Also called ADR. A list of design decisions with date, status and motivation. This allows going back to ADR to +understand +why choices have been made. Choices are not irrevocable, they can be deprecated in favor of new choices. The goal of +ADRs +is exactly to follow the evolution of these choices. + +### C4 + +Various C4 diagrams following [C4 model conventions](https://c4model.com/) + +### SequenceDiagrams + +Various sequence diagrams to help understand the flow of the application + +### SimulationOutputs + +Various example of simulation outputs + From 245cb9038930e3297f5088457b46fdcef1290256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jason=20Mar=C3=A9chal?= <45510813+JasonMarechal25@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:59:02 +0100 Subject: [PATCH 3/9] Use vcpkg for dependency management on all platforms (#770) - Change centos7 image management. Image is now antaresrte/xpansion-centos7 and tags follow semver schema - Migrate depedency management to vcpkg - Use action to setup cmake as much as possible - jsoncpp staticly linked to ease deploiment Comments and explanations: - _git fetch --unshallow_ is necessary because action checkout is shallow but vcpkg requires a "full" clone. - tbb is still manage manualy - jsoncpp is still link dynamicaly for MSVC due to difficulty to manage static/synamique with MSVC --- .github/workflows/build_centos7.yml | 84 +++++++++++++------ .github/workflows/build_oracle8.yml | 67 +++++++-------- .github/workflows/build_ubuntu.yml | 32 ++++--- .github/workflows/build_windows.yml | 8 +- .github/workflows/centos-release.yml | 73 ++++++++++------ .../workflows/centos7-system-deps-build.yml | 73 +++++++++------- .github/workflows/ol8-release.yml | 51 ++++++----- .github/workflows/publish_centos_docker.yml | 4 +- .github/workflows/ubuntu-release.yml | 36 +++++--- .../workflows/ubuntu-system-deps-build.yml | 43 +++++----- .../workflows/windows-vcpkg-deps-build.yml | 5 +- .github/workflows/windows-vcpkg.yml | 5 +- CMakeLists.txt | 37 ++++---- docker/centos7-system-deps | 5 +- .../benders/benders_by_batch/CMakeLists.txt | 4 - src/cpp/benders/benders_core/CMakeLists.txt | 7 +- src/cpp/benders/benders_mpi/BendersMPI.cpp | 2 +- .../benders/benders_sequential/CMakeLists.txt | 4 - src/cpp/benders/factories/BendersFactory.cpp | 5 +- src/cpp/benders/output/CMakeLists.txt | 5 +- src/cpp/helpers/CMakeLists.txt | 10 +-- src/cpp/sensitivity/CMakeLists.txt | 3 - tests/cpp/helpers/CMakeLists.txt | 2 + vcpkg.json | 7 +- 24 files changed, 335 insertions(+), 237 deletions(-) diff --git a/.github/workflows/build_centos7.yml b/.github/workflows/build_centos7.yml index da87fd5bd..c89f5014f 100644 --- a/.github/workflows/build_centos7.yml +++ b/.github/workflows/build_centos7.yml @@ -32,13 +32,13 @@ jobs: if: steps.changed-files.outputs.any_changed == 'true' uses: elgohr/Publish-Docker-Github-Action@main with: - name: antaresrte/rte-antares + name: antaresrte/xpansion-centos7 username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} workdir: docker dockerfile: centos7-system-deps cache: false - tags: centos7-system-deps + tags: latest,1.0.0 versions: runs-on: ubuntu-latest @@ -72,13 +72,13 @@ jobs: build: runs-on: ubuntu-latest needs: [ docker_publish, versions ] - container: 'antaresrte/rte-antares:centos7-system-deps' + container: 'antaresrte/xpansion-centos7' strategy: matrix: xprs: [ # { value: XPRESS-ON, ref: 8.13a }, - { value: XPRESS-ON, ref: 9.2.5 }, - # { value: XPRESS-OFF } + { value: XPRESS-ON, ref: 9.2.5 }, + #{ value: XPRESS-OFF } ] env: XPRESSDIR: ${{ github.workspace }}/xpress @@ -87,8 +87,12 @@ jobs: XPRESSDIR_CONTAINER: ${GITHUB_WORKSPACE}/xpress XPRESS_CONTAINER: ${GITHUB_WORKSPACE}/xpress/bin XPRS_LIB_Path_CONTAINER: ${GITHUB_WORKSPACE}/xpress/lib + VCPKG_ROOT: ${{ github.workspace }}/vcpkg steps: + - run: | + source /opt/rh/devtoolset-10/enable + echo $PATH >> $GITHUB_PATH - name: Get release if: github.event_name == 'release' && github.event.action == 'created' id: get_release @@ -98,7 +102,22 @@ jobs: with: submodules: true - - uses: ./.github/workflows/compile-gtest + - run: | + yum install -y nodejs + + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v1.13 + with: + cmake-version: '3.28.x' + + #Lukka's action doesn't work on runner. + #jwlawson's action doesn't work in ACT + #I left this here to quicly switch between one or the other + # - uses: lukka/get-cmake@latest + # with: + # useLocalCache: false + # useCloudCache: true + - name: Checkout xpressmp linux uses: actions/checkout@v3 #keep v3 with: @@ -109,6 +128,23 @@ jobs: ref: ${{matrix.xprs.ref}} if: matrix.xprs.value == 'XPRESS-ON' + - name: Restore vcpkg and its artifacts. + uses: actions/cache@v3 + with: + # The first path is the location of vcpkg (it contains the vcpkg executable and data files). + # The other paths starting with '!' are exclusions: they contain termporary files generated during the build of the installed packages. + path: | + ${{ env.VCPKG_ROOT }} + !${{ env.VCPKG_ROOT }}/buildtrees + !${{ env.VCPKG_ROOT }}/packages + !${{ env.VCPKG_ROOT }}/downloads + # The key is composed in a way that it gets properly invalidated: this must happen whenever vcpkg's Git commit id changes, or the list of packages changes. In this case a cache miss must happen and a new entry with a new key with be pushed to GitHub the cache service. + # The key includes: hash of the vcpkg.json file, the hash of the vcpkg Git commit id, and the used vcpkg's triplet. The vcpkg's commit id would suffice, but computing an hash out it does not harm. + # Note: given a key, the cache content is immutable. If a cache entry has been created improperly, in order the recreate the right content the key must be changed as well, and it must be brand new (i.e. not existing already). + key: | + ${{ hashFiles( 'vcpkg.json' ) }}-${{ hashFiles( '.git/modules/vcpkg/HEAD' )}}-${{ matrix.triplet }} + + - name: Download pre-compiled librairies uses: ./.github/workflows/download-extract-precompiled-libraries-tgz with: @@ -116,17 +152,9 @@ jobs: antares-version: ${{needs.versions.outputs.antares-version}} os: centos7 os-full-name: CentOS-7.9.2009 - #variant: -ortools-xpress - - - name: Compile Boost - uses: ./.github/workflows/compile-boost - with: - prefix: "../rte-antares-deps-Release/" - name: Compile tbb uses: ./.github/workflows/compile-tbb - with: - cmake: 'cmake3' - name: Install dependencies run: | @@ -134,13 +162,20 @@ jobs: pip3 install wheel #Does not work in requirements pip3 install -r requirements-tests.txt pip3 install -r requirements-ui.txt + + - name: vcpkg install + run: | + pushd vcpkg + git fetch --unshallow + ./bootstrap-vcpkg.sh + popd + vcpkg/vcpkg install + - name: Configure run: | [[ ${{ matrix.xprs.value }} == "XPRESS-ON" ]] && XPRESS_VALUE="ON" || XPRESS_VALUE="OFF" source /opt/rh/devtoolset-10/enable - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 -B _build -S . \ + cmake -B _build -S . \ -DDEPS_INSTALL_DIR=rte-antares-deps-Release \ -DBUILD_TESTING=ON \ -DCMAKE_BUILD_TYPE=Release \ @@ -148,20 +183,19 @@ jobs: -DBUILD_UI=ON \ -DXPRESS=${{ env.XPRESS_VALUE }} \ -DXPRESS_ROOT=${{ env.XPRESSDIR }} \ - -DALLOW_RUN_AS_ROOT=ON + -DALLOW_RUN_AS_ROOT=ON \ + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake + - name: Build run: | - source /opt/rh/devtoolset-10/enable - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 --build _build --config Release -j8 --target install + cmake --build _build --config Release -j$(nproc) + - name: Running unit tests timeout-minutes: 120 shell: bash run: | - source /etc/profile.d/modules.sh - module load mpi + export PATH=${GITHUB_WORKSPACE}/vcpkg_installed/x64-linux/tools/openmpi/bin/:$PATH export LD_LIBRARY_PATH=LD_LIBRARY_PATH:${{ env.XPRS_LIB_Path_CONTAINER }} export XPRESS=${{ env.XPRESS_CONTAINER }} cd _build - ctest3 -C Release --output-on-failure -L "unit|benders|lpnamer|medium" + ctest -C Release --output-on-failure -L "unit|benders|lpnamer|medium" diff --git a/.github/workflows/build_oracle8.yml b/.github/workflows/build_oracle8.yml index 132444be4..3cd3339b0 100644 --- a/.github/workflows/build_oracle8.yml +++ b/.github/workflows/build_oracle8.yml @@ -49,9 +49,10 @@ jobs: container: 'oraclelinux:8' strategy: matrix: - xprs: [ #{ value: XPRESS-ON, ref: 8.13a }, - { value: XPRESS-ON, ref: 9.2.5 }, - # { value: XPRESS-OFF } + xprs: [ + # { value: XPRESS-ON, ref: 8.13a }, + { value: XPRESS-ON, ref: 9.2.5 }, + # { value: XPRESS-OFF } ] needs: [ versions ] env: @@ -63,18 +64,26 @@ jobs: XPRS_LIB_Path_CONTAINER: ${GITHUB_WORKSPACE}/xpress/lib steps: - - name: Install System run: | - dnf install -y epel-release git cmake wget rpm-build redhat-lsb-core openmpi-devel - dnf install -y unzip libuuid-devel boost-test boost-devel gcc-toolset-10-toolchain zlib-devel python3-devel + dnf install -y epel-release git wget rpm-build redhat-lsb-core openmpi-devel + dnf install -y unzip libuuid-devel gcc-toolset-10-toolchain python3-devel zlib-devel + #make gcc &co available system wide and "action wide" + source /opt/rh/gcc-toolset-10/enable + echo $PATH >> $GITHUB_PATH + + - run: | + dnf module install -y nodejs:20/common + + - uses: lukka/get-cmake@latest + with: + useLocalCache: false + useCloudCache: true - uses: actions/checkout@v4 with: submodules: true - - uses: ./.github/workflows/compile-gtest - - name: Checkout xpressmp linux uses: actions/checkout@v4 with: @@ -89,9 +98,8 @@ jobs: run: | dnf update -y dnf install -y python3 python3-pip - - - run: - echo ${{needs.versions.outputs.antares-deps-version}} + pip3 install wheel #Too late to install in requirements.txt + pip3 install -r requirements-tests.txt - name: Download pre-compiled librairies uses: ./.github/workflows/download-extract-precompiled-libraries-tgz @@ -101,27 +109,24 @@ jobs: os: Oracle8 os-full-name: OracleServer-8.9 - - name: Compile Boost - uses: ./.github/workflows/compile-boost - with: - prefix: "../rte-antares-deps-Release/" - name: Compile TBB uses: ./.github/workflows/compile-tbb - - name: Install dependencies + - run: | + mkdir -p ${{ github.workspace }}/vcpkg_cache + + - name: vcpkg install run: | - source /opt/rh/gcc-toolset-10/enable - pip3 install wheel #Too late to install in requirements.txt - pip3 install -r requirements-tests.txt + pushd vcpkg + git fetch --unshallow + ./bootstrap-vcpkg.sh + popd + vcpkg/vcpkg install - name: Configure run: | [[ ${{ matrix.xprs.value }} == "XPRESS-ON" ]] && XPRESS_VALUE="ON" || XPRESS_VALUE="OFF" - source /opt/rh/gcc-toolset-10/enable - dnf install jsoncpp-devel - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 -B _build -S . \ + cmake -B _build -S . \ -DDEPS_INSTALL_DIR=rte-antares-deps-Release \ -DBUILD_TESTING=ON \ -DCMAKE_BUILD_TYPE=Release \ @@ -129,23 +134,19 @@ jobs: -DBUILD_UI=OFF \ -DXPRESS=${{ env.XPRESS_VALUE }} \ -DXPRESS_ROOT=${{ env.XPRESSDIR }} \ - -DALLOW_RUN_AS_ROOT=ON - + -DALLOW_RUN_AS_ROOT=ON \ + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - name: Build run: | - source /opt/rh/gcc-toolset-10/enable - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH - cmake --build _build --config Release -j2 + cmake --build _build --config Release -j$(nproc) - name: Running unit tests timeout-minutes: 120 shell: bash run: | - source /etc/profile.d/modules.sh - module load mpi + export PATH=${GITHUB_WORKSPACE}/vcpkg_installed/x64-linux/tools/openmpi/bin/:$PATH export LD_LIBRARY_PATH=LD_LIBRARY_PATH:${{ env.XPRS_LIB_Path_CONTAINER }} export XPRESS=${{ env.XPRESS_CONTAINER }} cd _build - ctest3 -C Release --output-on-failure -L "unit|benders|lpnamer|medium" + ctest -C Release --output-on-failure -L "unit|benders|lpnamer|medium" diff --git a/.github/workflows/build_ubuntu.yml b/.github/workflows/build_ubuntu.yml index 902bbbf97..918319f75 100644 --- a/.github/workflows/build_ubuntu.yml +++ b/.github/workflows/build_ubuntu.yml @@ -23,8 +23,8 @@ jobs: os: [ ubuntu-20.04 ] xprs: [ #{ value: XPRESS-ON, ref: 8.13a }, - { value: XPRESS-ON, ref: 9.2.5 }, - # { value: XPRESS-OFF } + { value: XPRESS-ON, ref: 9.2.5 }, + # { value: XPRESS-OFF } ] env: XPRESSDIR: ${{ github.workspace }}/xpress @@ -65,7 +65,7 @@ jobs: - name: Install mandatory system libraries run: | sudo apt-get update --fix-missing - sudo apt-get install -y ccache cmake libgtest-dev libjsoncpp-dev libtbb-dev libopenmpi-dev + sudo apt-get install -y ccache libtbb-dev sudo apt-get install -y g++-10 gcc-10 - name: Update alternatives @@ -78,6 +78,11 @@ jobs: sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++ 30 sudo update-alternatives --set c++ /usr/bin/g++ + - uses: lukka/get-cmake@latest + with: + useLocalCache: false + useCloudCache: true + - name: Read antares-solver version id: antares-version uses: ./.github/actions/read-json-value @@ -107,11 +112,16 @@ jobs: os: ${{matrix.os}} os-full-name: Ubuntu-20.04 - - name: Compile Boost - uses: ./.github/workflows/compile-boost - with: - prefix: "../rte-antares-deps-Release/" - load-toolset: 'false' + - run: | + mkdir -p ${{ github.workspace }}/vcpkg_cache + + - name: vcpkg install + run: | + pushd vcpkg + git fetch --unshallow + ./bootstrap-vcpkg.sh + popd + vcpkg/vcpkg install - name: Configure shell: bash @@ -129,13 +139,15 @@ jobs: -DCMAKE_INSTALL_PREFIX=_install \ -DBUILD_UI=ON \ -DXPRESS=${{ env.XPRESS_VALUE }} \ - -DXPRESS_ROOT=${{ env.XPRESSDIR }} + -DXPRESS_ROOT=${{ env.XPRESSDIR }} \ + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - name: Build run: | - cmake --build _build --config Release -j8 + cmake --build _build --config Release -j$(nproc) - name: Test run: | + export PATH=${GITHUB_WORKSPACE}/vcpkg_installed/x64-linux/tools/openmpi/bin/:$PATH cd _build ctest -C Release --output-on-failure -L "medium|unit|benders|lpnamer" diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index be2f6cfd8..b8277b5ff 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -23,7 +23,7 @@ jobs: os: [ windows-latest ] triplet: [ x64-windows ] xprs: [ #{ value: XPRESS-ON, ref: 8.13a }, - { value: XPRESS-ON, ref: 9.2.5 }, + { value: XPRESS-ON, ref: 9.2.5 }, #{ value: XPRESS-OFF } ] env: @@ -106,6 +106,7 @@ jobs: - name: Install deps with VCPKG run: | cd vcpkg + git fetch --unshallow ./bootstrap-vcpkg.sh vcpkg install --triplet ${{matrix.triplet}} rm -rf buildtrees @@ -135,10 +136,11 @@ jobs: - name: Configure run: | $pwd=Get-Location - cmake -B _build -S . -DDEPS_INSTALL_DIR=rte-antares-deps-Release -DCMAKE_PREFIX_PATH="$pwd\rte-antares-${{steps.antares-version.outputs.result}}-installer-64bits" -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_INSTALL_PREFIX=_install -DBUILD_UI=ON -DXPRESS=${{ env.XPRESS_VALUE }} -DXPRESS_ROOT="${{ env.XPRESSDIR }}" + cmake -B _build -S . -DDEPS_INSTALL_DIR=rte-antares-deps-Release -DCMAKE_PREFIX_PATH="$pwd\rte-antares-${{steps.antares-version.outputs.result}}-installer-64bits" -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_INSTALL_PREFIX=_install -DBUILD_UI=ON -DXPRESS=${{ env.XPRESS_VALUE }} -DXPRESS_ROOT="${{ env.XPRESSDIR }}" - name: Build run: | - cmake --build _build --config Release -j2 --target install + cmake --build _build --config Release -j4 + - name: Running unit tests timeout-minutes: 120 shell: cmd diff --git a/.github/workflows/centos-release.yml b/.github/workflows/centos-release.yml index 509140cc5..8a6cfbef5 100644 --- a/.github/workflows/centos-release.yml +++ b/.github/workflows/centos-release.yml @@ -40,13 +40,13 @@ jobs: if: steps.changed-files.outputs.any_changed == 'true' uses: elgohr/Publish-Docker-Github-Action@main with: - name: antaresrte/rte-antares + name: antaresrte/xpansion-centos7 username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} workdir: docker dockerfile: centos7-system-deps cache: false - tags: centos7-system-deps + tags: latest,1.0.0 userguide: runs-on: ubuntu-latest @@ -108,7 +108,7 @@ jobs: build: runs-on: ubuntu-latest needs: [ docker_publish, userguide, versions ] - container: 'antaresrte/rte-antares:centos7-system-deps' + container: 'antaresrte/xpansion-centos7' strategy: matrix: xprs: [ @@ -134,6 +134,18 @@ jobs: with: submodules: true + - run: | + source /opt/rh/devtoolset-10/enable + echo $PATH >> $GITHUB_PATH + + - run: | + yum install -y nodejs + + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v1.13 + with: + cmake-version: '3.28.x' + - name: Download pre-compiled librairies uses: ./.github/workflows/download-extract-precompiled-libraries-tgz with: @@ -141,18 +153,9 @@ jobs: antares-version: ${{needs.versions.outputs.antares-version}} os: centos7 os-full-name: CentOS-7.9.2009 - #variant: -ortools-xpress - - - uses: ./.github/workflows/compile-gtest - - name: Compile Boost - uses: ./.github/workflows/compile-boost - with: - prefix: "../rte-antares-deps-Release/" - name: Compile tbb uses: ./.github/workflows/compile-tbb - with: - cmake: 'cmake3' - name: Install dependencies run: | @@ -177,6 +180,31 @@ jobs: ref: 8.13a if: matrix.xprs == 'XPRESS-ON' + - name: Restore vcpkg and its artifacts. + uses: actions/cache@v3 + with: + # The first path is the location of vcpkg (it contains the vcpkg executable and data files). + # The other paths starting with '!' are exclusions: they contain termporary files generated during the build of the installed packages. + path: | + ${{ env.VCPKG_ROOT }} + !${{ env.VCPKG_ROOT }}/buildtrees + !${{ env.VCPKG_ROOT }}/packages + !${{ env.VCPKG_ROOT }}/downloads + # The key is composed in a way that it gets properly invalidated: this must happen whenever vcpkg's Git commit id changes, or the list of packages changes. In this case a cache miss must happen and a new entry with a new key with be pushed to GitHub the cache service. + # The key includes: hash of the vcpkg.json file, the hash of the vcpkg Git commit id, and the used vcpkg's triplet. The vcpkg's commit id would suffice, but computing an hash out it does not harm. + # Note: given a key, the cache content is immutable. If a cache entry has been created improperly, in order the recreate the right content the key must be changed as well, and it must be brand new (i.e. not existing already). + key: | + ${{ hashFiles( 'vcpkg.json' ) }}-${{ hashFiles( '.git/modules/vcpkg/HEAD' )}}-${{ matrix.triplet }} + + + - name: vcpkg install + run: | + pushd vcpkg + git fetch --unshallow + ./bootstrap-vcpkg.sh + popd + vcpkg/vcpkg install + - name: Configure shell: bash run: | @@ -186,9 +214,7 @@ jobs: export XPRESS_VALUE="OFF" fi source /opt/rh/devtoolset-10/enable - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 -B _build -S . \ + cmake -B _build -S . \ -DDEPS_INSTALL_DIR=rte-antares-deps-Release \ -DBUILD_TESTING=ON \ -DCMAKE_BUILD_TYPE=Release \ @@ -197,26 +223,25 @@ jobs: -DUSER_GUIDE_PATH="docs/${{ needs.userguide.outputs.pdf-name }}" \ -DXPRESS=${XPRESS_VALUE} \ -DXPRESS_ROOT=${XPRESSDIR} \ - -DALLOW_RUN_AS_ROOT=ON + -DALLOW_RUN_AS_ROOT=ON \ + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - name: Build shell: bash run: | - source /opt/rh/devtoolset-10/enable - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 --build _build --config Release -j2 --target install + cmake --build _build --config Release -j$(nproc) + cd _build + cmake --install . - name: Running unit tests timeout-minutes: 120 shell: bash run: | - source /etc/profile.d/modules.sh - module load mpi + export PATH=${GITHUB_WORKSPACE}/vcpkg_installed/x64-linux/tools/openmpi/bin/:$PATH export LD_LIBRARY_PATH=LD_LIBRARY_PATH:${{ env.XPRS_LIB_Path_CONTAINER }} export XPRESS=${{ env.XPRESS_CONTAINER }} cd _build - ctest3 -C Release --output-on-failure -L "unit|benders|lpnamer|medium" + ctest -C Release --output-on-failure -L "unit|benders|lpnamer|medium" - name: set name variables id: single_file_name @@ -234,7 +259,7 @@ jobs: run: | cd _build export FILE_NAME="antaresXpansion-${{env.VERSION_WITH_XPRESS}}-CentOS-7.9.2009" - cpack3 -G TGZ -D CPACK_PACKAGE_FILE_NAME=$FILE_NAME + cpack -G TGZ -D CPACK_PACKAGE_FILE_NAME=$FILE_NAME echo "TGZ_NAME=$FILE_NAME.tar.gz" >> $GITHUB_ENV - name: Upload .tar.gz diff --git a/.github/workflows/centos7-system-deps-build.yml b/.github/workflows/centos7-system-deps-build.yml index 7be482647..27ff365ed 100644 --- a/.github/workflows/centos7-system-deps-build.yml +++ b/.github/workflows/centos7-system-deps-build.yml @@ -27,13 +27,13 @@ jobs: if: steps.changed-files.outputs.any_changed == 'true' uses: elgohr/Publish-Docker-Github-Action@main with: - name: antaresrte/rte-antares + name: antaresrte/xpansion-centos7 username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} workdir: docker dockerfile: centos7-system-deps cache: false - tags: centos7-system-deps + tags: latest,1.0.0 versions: runs-on: ubuntu-latest @@ -68,7 +68,7 @@ jobs: runs-on: ubuntu-latest needs: [ docker_publish, versions ] - container: 'antaresrte/rte-antares:centos7-system-deps' + container: 'antaresrte/xpansion-centos7' steps: - id: branch-name @@ -78,59 +78,72 @@ jobs: with: submodules: true + - run: | + source /opt/rh/devtoolset-10/enable + echo $PATH >> $GITHUB_PATH + - name: Install dependencies run: | pip3 install wheel #Does not work in requirements pip3 install -r requirements-tests.txt - - uses: ./.github/workflows/compile-gtest - - name: Compile Boost - uses: ./.github/workflows/compile-boost + - run: | + yum install -y nodejs + + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v1.13 with: - prefix: "../rte-antares-deps-Release/" + cmake-version: '3.28.x' - name: Compile tbb - uses: ./.github/workflows/compile-tbb - with: - cmake: 'cmake3' + uses: ./.github/workflows/compile-tbb - - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.13 + - name: Restore vcpkg and its artifacts. + uses: actions/cache@v3 with: - cmake-version: '3.22.x' + # The first path is the location of vcpkg (it contains the vcpkg executable and data files). + # The other paths starting with '!' are exclusions: they contain termporary files generated during the build of the installed packages. + path: | + ${{ env.VCPKG_ROOT }} + !${{ env.VCPKG_ROOT }}/buildtrees + !${{ env.VCPKG_ROOT }}/packages + !${{ env.VCPKG_ROOT }}/downloads + # The key is composed in a way that it gets properly invalidated: this must happen whenever vcpkg's Git commit id changes, or the list of packages changes. In this case a cache miss must happen and a new entry with a new key with be pushed to GitHub the cache service. + # The key includes: hash of the vcpkg.json file, the hash of the vcpkg Git commit id, and the used vcpkg's triplet. The vcpkg's commit id would suffice, but computing an hash out it does not harm. + # Note: given a key, the cache content is immutable. If a cache entry has been created improperly, in order the recreate the right content the key must be changed as well, and it must be brand new (i.e. not existing already). + key: | + ${{ hashFiles( 'vcpkg.json' ) }}-${{ hashFiles( '.git/modules/vcpkg/HEAD' )}}-${{ matrix.triplet }} + + - name: vcpkg install + run: | + pushd vcpkg + git fetch --unshallow + ./bootstrap-vcpkg.sh + popd + vcpkg/vcpkg install - name: Configure run: | - source /opt/rh/devtoolset-10/enable - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH cmake -B _build -S . \ -DDEPS_INSTALL_DIR=rte-antares-deps-Release \ - -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=_install -DBUILD_UI=ON -DALLOW_RUN_AS_ROOT=ON + -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=_install -DBUILD_UI=ON -DALLOW_RUN_AS_ROOT=ON -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - name: Build run: | - source /opt/rh/devtoolset-10/enable - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH - cmake --build _build --config Release -j2 --target install + cmake --build _build --config Release -j$(nproc) + cd _build + cmake --install . - name: Running unit tests run: | - source /etc/profile.d/modules.sh - module load mpi + export PATH=${GITHUB_WORKSPACE}/vcpkg_installed/x64-linux/tools/openmpi/bin/:$PATH cd _build - ctest3 -C Release --output-on-failure -L "unit|benders|lpnamer|medium" + ctest -C Release --output-on-failure -L "unit|benders|lpnamer|medium" - name: .tar.gz creation run: | cd _build - cpack3 -G TGZ - - - name: Installer .rpm creation - run: | - cd _build - cpack3 -G RPM + cpack -G TGZ - id: create-single-file name: Single file .tar.gz creation diff --git a/.github/workflows/ol8-release.yml b/.github/workflows/ol8-release.yml index 12883b9f9..270c39129 100644 --- a/.github/workflows/ol8-release.yml +++ b/.github/workflows/ol8-release.yml @@ -102,16 +102,35 @@ jobs: - name: Install System run: | - dnf install -y epel-release git cmake wget rpm-build redhat-lsb-core openmpi-devel - dnf install -y unzip libuuid-devel boost-test boost-devel gcc-toolset-10-toolchain zlib-devel python3-devel + dnf install -y epel-release git wget rpm-build redhat-lsb-core + dnf install -y unzip libuuid-devel gcc-toolset-10-toolchain zlib-devel python3-devel source /opt/rh/gcc-toolset-10/enable - dnf install -y jsoncpp-devel + echo $PATH >> $GITHUB_PATH - name: Checkout uses: actions/checkout@v4 with: submodules: true + - run: | + dnf module install -y nodejs:20/common + + - uses: lukka/get-cmake@latest + with: + useLocalCache: false + useCloudCache: true + + - run: | + mkdir -p ${{ github.workspace }}/vcpkg_cache + + - name: vcpkg install + run: | + pushd vcpkg + git fetch --unshallow + ./bootstrap-vcpkg.sh + popd + vcpkg/vcpkg install + - name: Download pre-compiled librairies uses: ./.github/workflows/download-extract-precompiled-libraries-tgz with: @@ -121,16 +140,9 @@ jobs: os-full-name: OracleServer-8.9 #variant: -ortools-xpress - - name: Compile Boost - uses: ./.github/workflows/compile-boost - with: - prefix: "../rte-antares-deps-Release/" - - name: Compile tbb uses: ./.github/workflows/compile-tbb - - uses: ./.github/workflows/compile-gtest - - name: Install dependencies run: | pip3 install --upgrade pip @@ -165,7 +177,7 @@ jobs: source /opt/rh/gcc-toolset-10/enable export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 -B _build -S . \ + cmake -B _build -S . \ -DDEPS_INSTALL_DIR=rte-antares-deps-Release \ -DBUILD_TESTING=ON \ -DCMAKE_BUILD_TYPE=Release \ @@ -174,26 +186,25 @@ jobs: -DUSER_GUIDE_PATH="docs/${{ needs.userguide.outputs.pdf-name }}" \ -DXPRESS=${XPRESS_VALUE} \ -DXPRESS_ROOT=${XPRESSDIR} \ - -DALLOW_RUN_AS_ROOT=ON + -DALLOW_RUN_AS_ROOT=ON \ + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - name: Build shell: bash run: | - source /opt/rh/gcc-toolset-10/enable - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 --build _build --config Release -j2 --target install + cmake --build _build --config Release -j$(nproc) + cd _build + cmake --install . - name: Running unit tests timeout-minutes: 120 shell: bash run: | - source /etc/profile.d/modules.sh - module load mpi + export PATH=${GITHUB_WORKSPACE}/vcpkg_installed/x64-linux/tools/openmpi/bin/:$PATH export LD_LIBRARY_PATH=LD_LIBRARY_PATH:${{ env.XPRS_LIB_Path_CONTAINER }} export XPRESS=${{ env.XPRESS_CONTAINER }} cd _build - ctest3 -C Release --output-on-failure -L "unit|benders|lpnamer|medium" + ctest -C Release --output-on-failure -L "unit|benders|lpnamer|medium" - name: set name variables id: single_file_name @@ -211,7 +222,7 @@ jobs: run: | cd _build export FILE_NAME="antaresXpansion-${{env.VERSION_WITH_XPRESS}}-OracleServer-8.9" - cpack3 -G TGZ -D CPACK_PACKAGE_FILE_NAME=$FILE_NAME + cpack -G TGZ -D CPACK_PACKAGE_FILE_NAME=$FILE_NAME echo "TGZ_NAME=$FILE_NAME.tar.gz" >> $GITHUB_ENV - name: Upload .tar.gz diff --git a/.github/workflows/publish_centos_docker.yml b/.github/workflows/publish_centos_docker.yml index 12d7aa448..53284c09d 100644 --- a/.github/workflows/publish_centos_docker.yml +++ b/.github/workflows/publish_centos_docker.yml @@ -18,10 +18,10 @@ jobs: id: docker_push uses: elgohr/Publish-Docker-Github-Action@main with: - name: antaresrte/rte-antares + name: antaresrte/xpansion-centos7 username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} workdir: docker dockerfile: centos7-system-deps cache: false - tags: centos7-system-deps \ No newline at end of file + tags: latest,1.0.0 \ No newline at end of file diff --git a/.github/workflows/ubuntu-release.yml b/.github/workflows/ubuntu-release.yml index 102369ea3..1d7c317f5 100644 --- a/.github/workflows/ubuntu-release.yml +++ b/.github/workflows/ubuntu-release.yml @@ -98,11 +98,12 @@ jobs: - name: Install mandatory system libraries run: | sudo apt-get update --fix-missing - sudo apt-get install libjsoncpp-dev libgtest-dev libboost-mpi-dev libboost-program-options-dev libtbb-dev - cd /usr/src/googletest/ - sudo cmake . - sudo cmake --build . --target install - sudo apt-get install -y g++-10 gcc-10 + sudo apt-get install libtbb-dev + + - uses: lukka/get-cmake@latest + with: + useLocalCache: false + useCloudCache: true - name: Update alternatives #mpicxx uses "g++" so we need g++ to be symbolic link to g++-10 @@ -143,18 +144,23 @@ jobs: os: ${{matrix.os}} os-full-name: Ubuntu-20.04 - - name: Compile Boost - uses: ./.github/workflows/compile-boost - with: - prefix: "../rte-antares-deps-Release/" - load-toolset: 'false' - - name: Download userguide uses: actions/download-artifact@v3 with: name: user-guide path: docs/ + - run: | + mkdir -p ${{ github.workspace }}/vcpkg_cache + + - name: vcpkg install + run: | + pushd vcpkg + git fetch --unshallow + ./bootstrap-vcpkg.sh + popd + vcpkg/vcpkg install + - name: Configure shell: bash run: | @@ -173,11 +179,14 @@ jobs: -DBUILD_UI=ON \ -DUSER_GUIDE_PATH="docs/${{ needs.userguide.outputs.pdf-name }}" \ -DXPRESS=${{ env.XPRESS_VALUE }} \ - -DXPRESS_ROOT=${{ env.XPRESSDIR }} + -DXPRESS_ROOT=${{ env.XPRESSDIR }} \ + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - name: Build run: | - cmake --build _build --config Release -j8 --target install + cmake --build _build --config Release -j$(nproc) + cd _build + cmake --install . - name: set name variables id: single_file_name @@ -208,6 +217,7 @@ jobs: - name: Running unit tests run: | + export PATH=${GITHUB_WORKSPACE}/vcpkg_installed/x64-linux/tools/openmpi/bin/:$PATH cd _build ctest -C Release --output-on-failure -L "medium|unit|benders|lpnamer" diff --git a/.github/workflows/ubuntu-system-deps-build.yml b/.github/workflows/ubuntu-system-deps-build.yml index 1670a9c88..7e94a897b 100644 --- a/.github/workflows/ubuntu-system-deps-build.yml +++ b/.github/workflows/ubuntu-system-deps-build.yml @@ -40,11 +40,12 @@ jobs: - name: Install mandatory system libraries run: | sudo apt-get update --fix-missing - sudo apt-get install libjsoncpp-dev libgtest-dev libboost-mpi-dev libboost-program-options-dev libtbb-dev - cd /usr/src/googletest/ - sudo cmake . - sudo cmake --build . --target install - sudo apt-get install -y g++-10 gcc-10 + sudo apt-get install libtbb-dev + + - uses: lukka/get-cmake@latest + with: + useLocalCache: false + useCloudCache: true - name: Update alternatives #mpicxx uses "g++" so we need g++ to be symbolic link to g++-10 @@ -56,13 +57,6 @@ jobs: sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++ 30 sudo update-alternatives --set c++ /usr/bin/g++ - - name: Compile Boost - uses: ./.github/workflows/compile-boost - with: - prefix: "../rte-antares-deps-Release/" - load-toolset: 'false' - - - name: Read antares-xpansion version id: antares-xpansion-version uses: ./.github/actions/read-json-value @@ -70,6 +64,17 @@ jobs: path: 'antares-version.json' key: 'antares_xpansion_version' + - run: | + mkdir -p ${{ github.workspace }}/vcpkg_cache + + - name: vcpkg install + run: | + pushd vcpkg + git fetch --unshallow + ./bootstrap-vcpkg.sh + popd + vcpkg/vcpkg install + - name: Configure run: | cmake -B _build -S . \ @@ -77,14 +82,18 @@ jobs: -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=_install \ - -DBUILD_UI=ON + -DBUILD_UI=ON \ + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake - name: Build run: | - cmake --build _build --config Release -j2 --target install + cmake --build _build --config Release -j$(nproc) + cd _build + cmake --install . - name: Running unit tests run: | + export PATH=${GITHUB_WORKSPACE}/vcpkg_installed/x64-linux/tools/openmpi/bin/:$PATH cd _build ctest -C Release --output-on-failure -L "medium|unit|benders|lpnamer" @@ -98,9 +107,3 @@ jobs: run: | cd _build cpack -G TGZ - - - name: Installer .deb creation - run: | - cd _build - cpack -G DEB - diff --git a/.github/workflows/windows-vcpkg-deps-build.yml b/.github/workflows/windows-vcpkg-deps-build.yml index 594c9a94f..a15055b59 100644 --- a/.github/workflows/windows-vcpkg-deps-build.yml +++ b/.github/workflows/windows-vcpkg-deps-build.yml @@ -76,6 +76,7 @@ jobs: - name: Install deps with VCPKG run: | cd vcpkg + git fetch --unshallow ./bootstrap-vcpkg.sh vcpkg install --triplet ${{matrix.triplet}} rm -rf buildtrees @@ -95,7 +96,9 @@ jobs: - name: Build run: | - cmake --build _build --config Release -j2 --target install + cmake --build _build --config Release -j4 + cd _build + cmake --install . - name: Running unit tests shell: cmd diff --git a/.github/workflows/windows-vcpkg.yml b/.github/workflows/windows-vcpkg.yml index aa8418099..8fc06fe7c 100644 --- a/.github/workflows/windows-vcpkg.yml +++ b/.github/workflows/windows-vcpkg.yml @@ -150,6 +150,7 @@ jobs: - name: Install deps with VCPKG run: | cd vcpkg + git fetch --unshallow ./bootstrap-vcpkg.sh vcpkg install --triplet ${{matrix.triplet}} rm -rf buildtrees @@ -190,7 +191,9 @@ jobs: - name: Build run: | - cmake --build _build --config Release -j2 --target install + cmake --build _build --config Release -j4 + cd _build + cmake --install . - name: Running unit tests shell: cmd diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fb9091e3..bde157b08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,16 +74,15 @@ if (MSVC) message ("WINDOWS") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") - set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") set (RUNTIME_IGNORE_LIST_RELEASE "/NODEFAULTLIB:msvcrtd.lib /NODEFAULTLIB:msvcprtd.lib /NODEFAULTLIB:libcmtd.lib") set (CMAKE_EXE_LINKER_FLAGS_RELEASE " ${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${RUNTIME_IGNORE_LIST_RELEASE}") set (CMAKE_SHARED_LINKER_FLAGS_RELEASE " ${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${RUNTIME_IGNORE_LIST_RELEASE}") set (CMAKE_STATIC_LINKER_FLAGS_RELEASE " ${CMAKE_STATIC_LINKER_FLAGS_RELEASE} ${RUNTIME_IGNORE_LIST_RELEASE}") - - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") + + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /EHsc") - - + else () set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") @@ -235,8 +234,18 @@ if (SOLVER) endif() find_package(jsoncpp CONFIG REQUIRED) - - +macro(jsoncpplib_name libname) + if (MSVC) + set(${libname} jsoncpp_lib) + else () + set(${libname} jsoncpp_static) + endif () +endmacro() +jsoncpplib_name(jsonlib) +install(FILES + $ + DESTINATION bin +) ## Coin-OR (Clp and CBC solvers) if(COIN_OR) @@ -324,20 +333,6 @@ install(DIRECTORY ${ANTARES_SOLVER_DIR}/../lib/ install(PROGRAMS ${ANTARES_SOLVER_PATH} TYPE BIN) -find_package(antares-study-updater) -if (antares-study-updater_FOUND) - - if (${CMAKE_BUILD_TYPE} STREQUAL "Release") - get_target_property(ANTARES_STUDY_UPDATER_PATH antares-${ANTARES_VERSION}-study-updater IMPORTED_LOCATION_RELEASE) - else () - get_target_property(ANTARES_STUDY_UPDATER_PATH antares-${ANTARES_VERSION}-study-updater IMPORTED_LOCATION_DEBUG) - endif () - - install(PROGRAMS ${ANTARES_STUDY_UPDATER_PATH} - TYPE BIN) - -endif () - include (InstallRequiredSystemLibraries) if(CMAKE_SYSTEM_NAME MATCHES "Linux") diff --git a/docker/centos7-system-deps b/docker/centos7-system-deps index b534fed80..cf2e7b824 100644 --- a/docker/centos7-system-deps +++ b/docker/centos7-system-deps @@ -8,9 +8,8 @@ RUN yum install -y epel-release RUN \ yum install -y wget git epel-release redhat-lsb-core gcc gcc-c++ make centos-release-scl scl-utils &&\ - yum install -y cmake3 devtoolset-10-gcc* environment-modules rpm-build zlib-devel &&\ - yum install -y jsoncpp-devel openmpi-devel doxygen graphviz &&\ - yum install -y gtest-devel &&\ + yum install -y devtoolset-10-gcc* environment-modules rpm-build zlib-devel &&\ + yum install -y doxygen graphviz &&\ yum install -y install python3-devel && \ yum install -y libuuid-devel diff --git a/src/cpp/benders/benders_by_batch/CMakeLists.txt b/src/cpp/benders/benders_by_batch/CMakeLists.txt index 56128aebc..096caf8f2 100644 --- a/src/cpp/benders/benders_by_batch/CMakeLists.txt +++ b/src/cpp/benders/benders_by_batch/CMakeLists.txt @@ -21,15 +21,11 @@ add_library (benders_by_batch_core STATIC ${CMAKE_CURRENT_SOURCE_DIR}/RandomBatchShuffler.cpp ) -get_target_property(JSON_INC_PATH jsoncpp_lib INTERFACE_INCLUDE_DIRECTORIES) - - target_include_directories (benders_by_batch_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - ${JSON_INC_PATH}/jsoncpp ) target_link_libraries (benders_by_batch_core diff --git a/src/cpp/benders/benders_core/CMakeLists.txt b/src/cpp/benders/benders_core/CMakeLists.txt index e3f38ffac..d2434990c 100644 --- a/src/cpp/benders/benders_core/CMakeLists.txt +++ b/src/cpp/benders/benders_core/CMakeLists.txt @@ -29,16 +29,13 @@ add_library (benders_core STATIC ${CMAKE_CURRENT_SOURCE_DIR}/BendersMathLogger.cpp ) -get_target_property(JSON_INC_PATH jsoncpp_lib INTERFACE_INCLUDE_DIRECTORIES) - - target_include_directories (benders_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include - ${JSON_INC_PATH}/jsoncpp ) +jsoncpplib_name(jsonlib) target_link_libraries (benders_core PUBLIC helpers @@ -46,7 +43,7 @@ target_link_libraries (benders_core solvers glog::glog TBB::tbb - jsoncpp_lib + ${jsonlib} ) add_library (${PROJECT_NAME}::benders_core ALIAS benders_core) \ No newline at end of file diff --git a/src/cpp/benders/benders_mpi/BendersMPI.cpp b/src/cpp/benders/benders_mpi/BendersMPI.cpp index bf2859bc7..a4088a98a 100644 --- a/src/cpp/benders/benders_mpi/BendersMPI.cpp +++ b/src/cpp/benders/benders_mpi/BendersMPI.cpp @@ -322,4 +322,4 @@ void BendersMpi::launch() { free(); } _world.barrier(); -} +} \ No newline at end of file diff --git a/src/cpp/benders/benders_sequential/CMakeLists.txt b/src/cpp/benders/benders_sequential/CMakeLists.txt index 73ded7159..68741f1a1 100644 --- a/src/cpp/benders/benders_sequential/CMakeLists.txt +++ b/src/cpp/benders/benders_sequential/CMakeLists.txt @@ -10,15 +10,11 @@ add_library (benders_sequential_core STATIC ${CMAKE_CURRENT_SOURCE_DIR}/BendersSequential.cpp ) -get_target_property(JSON_INC_PATH jsoncpp_lib INTERFACE_INCLUDE_DIRECTORIES) - - target_include_directories (benders_sequential_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - ${JSON_INC_PATH}/jsoncpp ) target_link_libraries (benders_sequential_core diff --git a/src/cpp/benders/factories/BendersFactory.cpp b/src/cpp/benders/factories/BendersFactory.cpp index 6231a1f7b..45f808ee6 100644 --- a/src/cpp/benders/factories/BendersFactory.cpp +++ b/src/cpp/benders/factories/BendersFactory.cpp @@ -197,7 +197,6 @@ int RunExternalLoop_(char** argv, const std::filesystem::path& options_file, } BendersMainFactory::BendersMainFactory(int argc, char** argv, - mpi::environment& env, mpi::communicator& world) : argv_(argv), penv_(&env), pworld_(&world) { @@ -212,7 +211,9 @@ BendersMainFactory::BendersMainFactory(int argc, char** argv, BendersMainFactory::BendersMainFactory( int argc, char** argv, const std::filesystem::path& options_file, mpi::environment& env, mpi::communicator& world) - : argv_(argv), options_file_(options_file), penv_(&env), pworld_(&world) { + : argv_(argv), options_file_(options_file), + penv_(&env), + pworld_(&world) { // First check usage (options are given) if (world.rank() == 0) { usage(argc); diff --git a/src/cpp/benders/output/CMakeLists.txt b/src/cpp/benders/output/CMakeLists.txt index 3882a62f5..039a9879f 100644 --- a/src/cpp/benders/output/CMakeLists.txt +++ b/src/cpp/benders/output/CMakeLists.txt @@ -11,7 +11,6 @@ add_library (output_core STATIC ${CMAKE_CURRENT_SOURCE_DIR}/JsonWriter.cpp ) -get_target_property(JSON_INC_PATH jsoncpp_lib INTERFACE_INCLUDE_DIRECTORIES) get_target_property(xpansion_interfaces_path xpansion_interfaces INTERFACE_INCLUDE_DIRECTORIES) target_include_directories (output_core @@ -19,13 +18,13 @@ target_include_directories (output_core ${CMAKE_CURRENT_SOURCE_DIR}/include PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - ${JSON_INC_PATH}/jsoncpp ${xpansion_interfaces_path} ) +jsoncpplib_name(jsonlib) target_link_libraries (output_core PUBLIC - jsoncpp_lib + ${jsonlib} helpers ) diff --git a/src/cpp/helpers/CMakeLists.txt b/src/cpp/helpers/CMakeLists.txt index f3e94f5e9..08812bd5a 100644 --- a/src/cpp/helpers/CMakeLists.txt +++ b/src/cpp/helpers/CMakeLists.txt @@ -38,27 +38,21 @@ add_library (helpers STATIC ${CMAKE_CURRENT_SOURCE_DIR}/LoggerUtils.h ) -get_target_property(JSON_INC_PATH jsoncpp_lib INTERFACE_INCLUDE_DIRECTORIES) get_target_property(xpansion_interfaces_path xpansion_interfaces INTERFACE_INCLUDE_DIRECTORIES) target_include_directories (helpers PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - ${JSON_INC_PATH}/jsoncpp ${xpansion_interfaces_path} ) +jsoncpplib_name(jsonlib) target_link_libraries (helpers PUBLIC - jsoncpp_lib + ${jsonlib} solvers glog::glog gflags::gflags Boost::boost Boost::program_options MINIZIP::minizip-ng ) - -install(FILES - $ - DESTINATION bin -) diff --git a/src/cpp/sensitivity/CMakeLists.txt b/src/cpp/sensitivity/CMakeLists.txt index ee1ecdc6d..fcfd0d0b0 100644 --- a/src/cpp/sensitivity/CMakeLists.txt +++ b/src/cpp/sensitivity/CMakeLists.txt @@ -19,13 +19,10 @@ add_library (sensitivity_core Analysis.h Analysis.cpp) -get_target_property(JSON_INC_PATH jsoncpp_lib -INTERFACE_INCLUDE_DIRECTORIES) target_include_directories (sensitivity_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include - ${JSON_INC_PATH}/jsoncpp ) target_link_libraries (sensitivity_core diff --git a/tests/cpp/helpers/CMakeLists.txt b/tests/cpp/helpers/CMakeLists.txt index 4479f1c32..cff4acc9c 100644 --- a/tests/cpp/helpers/CMakeLists.txt +++ b/tests/cpp/helpers/CMakeLists.txt @@ -11,6 +11,7 @@ target_include_directories (helpers_test ${CMAKE_CURRENT_SOURCE_DIR} ) +jsoncpplib_name(jsonlib) target_link_libraries (helpers_test PRIVATE GTest::Main helpers @@ -18,6 +19,7 @@ target_link_libraries (helpers_test PRIVATE lp_namer_model lp_namer_input_reader lp_namer_problem_modifier + ${jsonlib} ${PROJECT_NAME}::benders_sequential_core ) diff --git a/vcpkg.json b/vcpkg.json index e8d86672d..35e3b00cf 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,11 +1,16 @@ { "name": "antares-xpansion", - "version-string": "0.4.0", + "version-string": "1.2.2", + "builtin-baseline": "9484a57dd560b89f0a583be08af6753611c57fd5", "dependencies": [ "jsoncpp", "gtest", "boost-mpi", "boost-program-options", "zlib" + ], + "overrides": [ + { "name": "boost-mpi", "version": "1.81.0" }, + { "name": "boost-program-options", "version": "1.81.0" } ] } From 731f2f89183ddfa7df31c06672869c34c4ad61ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jason=20Mar=C3=A9chal?= <45510813+JasonMarechal25@users.noreply.github.com> Date: Mon, 8 Apr 2024 11:35:25 +0200 Subject: [PATCH 4/9] Fix sonar (#773) Sonar scanner has been updated from 4.X to 5.x Updating cache key to reflect this --- .github/workflows/sonarcloud.yml | 27 +++++++++++++-------------- sonar-project.properties | 3 +-- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 960fee6e9..008784532 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -18,27 +18,23 @@ jobs: matrix: os: [ ubuntu-20.04 ] - env: - SONAR_SCANNER_VERSION: 4.7.0.2747 # Find the latest version in the "Linux" link on this page: - # https://sonarcloud.io/documentation/analysis/scan/sonarscanner/ - SONAR_SERVER_URL: "https://sonarcloud.io" - steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Install sonar-scanner and build-wrapper + - id: sonar-install + name: Install sonar-scanner and build-wrapper uses: SonarSource/sonarcloud-github-c-cpp@v2 - name: ccache uses: hendrikmuhs/ccache-action@v1.2 with: - key: sonarcloud-${{ env.SONAR_SCANNER_VERSION }} + key: ${{ matrix.os }}-${{ matrix.xprs.value }} - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v4 with: python-version: 3.8 @@ -102,8 +98,6 @@ jobs: uses: ./.github/workflows/compile-boost with: prefix: "../rte-antares-deps-Release/" - load-toolset: 'false' - - name: Init submodule run: | @@ -122,10 +116,15 @@ jobs: -DBUILD_TESTING=ON \ -DBUILD_antares_solver=OFF \ -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=_install + -DCMAKE_INSTALL_PREFIX=_install \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake + + - run: | + sed -i 's#: ".*/mpicxx #: "g++ #' $GITHUB_WORKSPACE/_build/compile_commands.json - name: Build - run: build-wrapper-linux-x86-64 --out-dir $GITHUB_WORKSPACE/_build/output cmake --build _build --config Release -j2 + run: cmake --build _build --config Release -j$(nproc) - name: Test and generate coverage continue-on-error: true @@ -141,4 +140,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_2022 }} - run: sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" \ No newline at end of file + run: sonar-scanner --define sonar.host.url="${{ env.SONAR_HOST_URL }}" \ No newline at end of file diff --git a/sonar-project.properties b/sonar-project.properties index 1481123a3..89362ace3 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -13,10 +13,9 @@ sonar.sourceEncoding=UTF-8 sonar.exclusions=src/cpp/lpnamer/input_reader/INIReader.h -sonar.cfamily.build-wrapper-output=_build/output sonar.cfamily.analysisCache.mode=server sonar.coverageReportPaths=_build/coverage/coverage.xml -sonar.cfamily.threads=2 +sonar.cfamily.compile-commands=_build/compile_commands.json sonar.python.coverage.reportPaths=tests/python/coverage-reports/coverage-*.xml sonar.python.version=3.8 \ No newline at end of file From 70bf39927375c3db099a1ff1ea642ab5fb08baf0 Mon Sep 17 00:00:00 2001 From: abdoulbari zakir <32519851+a-zakir@users.noreply.github.com> Date: Mon, 8 Apr 2024 11:37:44 +0200 Subject: [PATCH 5/9] fix single file name (#785) there is a bug in windows release workflows, the single file archive is not uploaded. --- .github/workflows/windows-vcpkg.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/windows-vcpkg.yml b/.github/workflows/windows-vcpkg.yml index 8fc06fe7c..5d5ec4f9b 100644 --- a/.github/workflows/windows-vcpkg.yml +++ b/.github/workflows/windows-vcpkg.yml @@ -68,6 +68,7 @@ jobs: VCPKG_ROOT: ${{ github.workspace }}/vcpkg outputs: zip_name: ${{ steps.zip_name.outputs.zip_name }} + singlefile_name: ${{steps.zip_name.outputs.singlefile_name}} steps: - uses: actions/checkout@v4 with: From c33f5f8134958cd1649026431aa7073b0e67c7bc Mon Sep 17 00:00:00 2001 From: abdoulbari zaher <32519851+a-zakir@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:25:39 +0200 Subject: [PATCH 6/9] fix xpress detection spam (#771) In mpi parallel mode, xpress detection infos are shown multiple times (x nums of procs) this pr should fix this --- src/cpp/benders/benders_core/Worker.cpp | 2 +- .../benders_core/include/BendersMathLogger.h | 6 +- .../helper/ProblemGenerationLogger.cpp | 32 +++- .../lpnamer/helper/ProblemGenerationLogger.h | 33 ++-- src/cpp/lpnamer/main/ProblemGeneration.cpp | 4 +- src/cpp/multisolver_interface/SolverCbc.cpp | 4 +- src/cpp/multisolver_interface/SolverClp.cpp | 3 +- .../multisolver_interface/SolverFactory.cpp | 11 +- .../multisolver_interface/SolverXpress.cpp | 7 +- src/cpp/multisolver_interface/environment.cc | 146 +++++++++++------- .../multisolver_interface/SolverFactory.h | 13 +- .../multisolver_interface/environment.h | 37 ++++- src/cpp/xpansion_interfaces/ILogger.h | 49 +++++- tests/cpp/ext_loop/ext_loop_test.cpp | 3 +- .../cpp/sensitivity/SensitivityStudyTest.cpp | 3 +- 15 files changed, 252 insertions(+), 101 deletions(-) diff --git a/src/cpp/benders/benders_core/Worker.cpp b/src/cpp/benders/benders_core/Worker.cpp index 2859df599..51a0ec958 100644 --- a/src/cpp/benders/benders_core/Worker.cpp +++ b/src/cpp/benders/benders_core/Worker.cpp @@ -39,7 +39,7 @@ void Worker::init(VariableMap const &variable_map, std::string const &solver_name, int log_level, SolverLogManager&solver_log_manager) { _path_to_mps = path_to_mps; - SolverFactory factory; + SolverFactory factory(logger_); if (_is_master) { _solver = factory.create_solver(solver_name, SOLVER_TYPE::INTEGER, diff --git a/src/cpp/benders/benders_core/include/BendersMathLogger.h b/src/cpp/benders/benders_core/include/BendersMathLogger.h index 3bee621a2..53baf65d4 100644 --- a/src/cpp/benders/benders_core/include/BendersMathLogger.h +++ b/src/cpp/benders/benders_core/include/BendersMathLogger.h @@ -56,7 +56,7 @@ void PrintExternalLoopData(LogDestination& log_destination, const HEADERSTYPE& type, const BENDERSMETHOD& method); -struct MathLoggerBehaviour : public ILoggerBenders { +struct MathLoggerBehaviour : public ILoggerXpansion { void write_header() { setHeadersList(); for (const auto& header : Headers()) { @@ -65,7 +65,7 @@ struct MathLoggerBehaviour : public ILoggerBenders { LogsDestination() << std::endl; } - virtual void display_message(const std::string& str) { + void display_message(const std::string& str) override { LogsDestination() << str << std::endl; } @@ -166,7 +166,7 @@ class MathLoggerImplementation : public MathLoggerBehaviour { std::shared_ptr implementation_; }; -class MathLoggerDriver : public ILoggerBenders { +class MathLoggerDriver : public ILoggerXpansion { public: MathLoggerDriver() = default; void write_header(); diff --git a/src/cpp/lpnamer/helper/ProblemGenerationLogger.cpp b/src/cpp/lpnamer/helper/ProblemGenerationLogger.cpp index 37ce42b37..361361e10 100644 --- a/src/cpp/lpnamer/helper/ProblemGenerationLogger.cpp +++ b/src/cpp/lpnamer/helper/ProblemGenerationLogger.cpp @@ -14,11 +14,17 @@ ProblemGenerationFileLogger::ProblemGenerationFileLogger( << logPath.string() << ") passed as parameter" << std::endl; } } -void ProblemGenerationFileLogger::DisplayMessage(const std::string& message) { +void ProblemGenerationFileLogger::display_message(const std::string& message) { logFile_ << message << std::endl; logFile_.flush(); } +std::ostream& ProblemGenerationFileLogger::GetOstreamObject() { + return logFile_; +} +void ProblemGenerationFileLogger::PrintIterationSeparatorBegin() {} +void ProblemGenerationFileLogger::PrintIterationSeparatorEnd() {} + ProblemGenerationOstreamLogger::ProblemGenerationOstreamLogger( std::ostream& stream) : stream_(stream) { @@ -29,27 +35,37 @@ ProblemGenerationOstreamLogger::ProblemGenerationOstreamLogger( << std::endl; } } -void ProblemGenerationOstreamLogger::DisplayMessage( +void ProblemGenerationOstreamLogger::display_message( const std::string& message) { stream_ << message << std::endl; } + +void ProblemGenerationOstreamLogger::PrintIterationSeparatorBegin() {} +void ProblemGenerationOstreamLogger::PrintIterationSeparatorEnd() {} +std::ostream& ProblemGenerationOstreamLogger::GetOstreamObject() { + return stream_; +} + void ProblemGenerationLogger::AddLogger( const ProblemGenerationILoggerSharedPointer& logger) { loggers_.push_back(logger); try_to_add_logger_to_enabled_list(logger); } -void ProblemGenerationLogger::DisplayMessage(const std::string& message) const { +void ProblemGenerationLogger::display_message(const std::string& message) { for (const auto& logger : enabled_loggers_) { - logger->DisplayMessage(message); + logger->display_message(message); } } -void ProblemGenerationLogger::DisplayMessage( - const std::string& message, const LogUtils::LOGLEVEL log_level) const { +void ProblemGenerationLogger::display_message( + const std::string& message, const LogUtils::LOGLEVEL log_level) { for (const auto& logger : enabled_loggers_) { - logger->DisplayMessage(LogUtils::LogLevelToStr(log_level)); - logger->DisplayMessage(message); + logger->display_message(LogUtils::LogLevelToStr(log_level)); + logger->display_message(message); } } + +void ProblemGenerationLogger::PrintIterationSeparatorBegin() {} +void ProblemGenerationLogger::PrintIterationSeparatorEnd() {} void ProblemGenerationLogger::setLogLevel(const LogUtils::LOGLEVEL log_level) { log_level_ = log_level; update_enabled_logger(); diff --git a/src/cpp/lpnamer/helper/ProblemGenerationLogger.h b/src/cpp/lpnamer/helper/ProblemGenerationLogger.h index 2a010473a..bead3add9 100644 --- a/src/cpp/lpnamer/helper/ProblemGenerationLogger.h +++ b/src/cpp/lpnamer/helper/ProblemGenerationLogger.h @@ -11,14 +11,19 @@ #include #include +#include "ILogger.h" #include "LogUtils.h" #include "LoggerUtils.h" + namespace ProblemGenerationLog { -class ProblemGenerationILogger { +class ProblemGenerationILogger : public ILoggerXpansion { public: - virtual ~ProblemGenerationILogger() = default; - virtual void DisplayMessage(const std::string& message) = 0; + ~ProblemGenerationILogger() override = default; + void display_message(const std::string& message) override = 0; + void PrintIterationSeparatorBegin() override = 0; + void PrintIterationSeparatorEnd() override = 0; + virtual std::ostream& GetOstreamObject() = 0; LogUtils::LOGGERTYPE Type() const { return type_; } @@ -39,8 +44,10 @@ class ProblemGenerationFileLogger : public ProblemGenerationILogger { ~ProblemGenerationFileLogger() override { logFile_.close(); } explicit ProblemGenerationFileLogger( const std::filesystem::path& logFilePath); - void DisplayMessage(const std::string& message) override; - std::ostream& GetOstreamObject() override { return logFile_; } + void display_message(const std::string& message) override; + void PrintIterationSeparatorBegin() override; + void PrintIterationSeparatorEnd() override; + std::ostream& GetOstreamObject() override; }; class ProblemGenerationOstreamLogger : public ProblemGenerationILogger { @@ -50,15 +57,17 @@ class ProblemGenerationOstreamLogger : public ProblemGenerationILogger { public: ~ProblemGenerationOstreamLogger() override = default; explicit ProblemGenerationOstreamLogger(std::ostream& stream); - void DisplayMessage(const std::string& message) override; - std::ostream& GetOstreamObject() override { return stream_; } + void display_message(const std::string& message) override; + void PrintIterationSeparatorBegin() override; + void PrintIterationSeparatorEnd() override; + std::ostream& GetOstreamObject() override; }; class ProblemGenerationLogger; using ProblemGenerationLoggerSharedPointer = std::shared_ptr; -class ProblemGenerationLogger { +class ProblemGenerationLogger : public ILoggerXpansion { private: LogUtils::LOGLEVEL log_level_; std::string context_ = "Unknown Context"; @@ -69,9 +78,11 @@ class ProblemGenerationLogger { ~ProblemGenerationLogger() = default; void AddLogger(const ProblemGenerationILoggerSharedPointer& logger); - void DisplayMessage(const std::string& message) const; - void DisplayMessage(const std::string& message, - const LogUtils::LOGLEVEL log_level) const; + void display_message(const std::string& message); + void display_message(const std::string& message, + const LogUtils::LOGLEVEL log_level); + void PrintIterationSeparatorBegin() override; + void PrintIterationSeparatorEnd() override; void setLogLevel(const LogUtils::LOGLEVEL log_level); void setContext(const std::string& context) { context_ = context; } diff --git a/src/cpp/lpnamer/main/ProblemGeneration.cpp b/src/cpp/lpnamer/main/ProblemGeneration.cpp index 7380e08f4..fcb5c77f2 100644 --- a/src/cpp/lpnamer/main/ProblemGeneration.cpp +++ b/src/cpp/lpnamer/main/ProblemGeneration.cpp @@ -159,8 +159,8 @@ void ProblemGeneration::RunProblemGeneration( validateMasterFormulation(master_formulation, logger); std::string solver_name = "CBC"; // TODO Use solver selected by user - SolverLoader::GetAvailableSolvers(); // Dirty fix to populate static value - // outside multi thread code + SolverLoader::GetAvailableSolvers(logger); // Dirty fix to populate static + // value outside multi thread code Timer problem_generation_timer; if (!weights_file.empty()) { ProcessWeights(xpansion_output_dir, antares_archive_path, weights_file, diff --git a/src/cpp/multisolver_interface/SolverCbc.cpp b/src/cpp/multisolver_interface/SolverCbc.cpp index 06a9480e4..bf1ebb472 100644 --- a/src/cpp/multisolver_interface/SolverCbc.cpp +++ b/src/cpp/multisolver_interface/SolverCbc.cpp @@ -1,6 +1,7 @@ #include "SolverCbc.h" #include "COIN_common_functions.h" +using namespace std::literals; /************************************************************************************************* ----------------------------------- Constructor/Desctructor @@ -183,7 +184,8 @@ void SolverCbc::setClpSimplexRowNamesFromInnerSolver(ClpSimplex *clps) const { void SolverCbc::read_prob_mps(const std::filesystem::path &prob_name) { int status = _clp_inner_solver.readMps(prob_name.string().c_str()); - zero_status_check(status, "read problem", LOGLOCATION); + zero_status_check(status, " read problem "s + prob_name.string(), + LOGLOCATION); defineCbcModelFromInnerSolver(); } diff --git a/src/cpp/multisolver_interface/SolverClp.cpp b/src/cpp/multisolver_interface/SolverClp.cpp index 761f9e180..b4c0c16f9 100644 --- a/src/cpp/multisolver_interface/SolverClp.cpp +++ b/src/cpp/multisolver_interface/SolverClp.cpp @@ -1,6 +1,7 @@ #include "SolverClp.h" #include "COIN_common_functions.h" +using namespace std::literals; /************************************************************************************************* ----------------------------------- Constructor/Desctructor @@ -84,7 +85,7 @@ void SolverClp::write_basis(const std::filesystem::path &filename) { void SolverClp::read_prob_mps(const std::filesystem::path &filename) { int status = _clp.readMps(filename.string().c_str(), true, false); - zero_status_check(status, "Clp readMps", LOGLOCATION); + zero_status_check(status, " Clp readMps "s + filename.string(), LOGLOCATION); } void SolverClp::read_prob_lp(const std::filesystem::path &filename) { diff --git a/src/cpp/multisolver_interface/SolverFactory.cpp b/src/cpp/multisolver_interface/SolverFactory.cpp index fb38d45b1..7857f2c72 100644 --- a/src/cpp/multisolver_interface/SolverFactory.cpp +++ b/src/cpp/multisolver_interface/SolverFactory.cpp @@ -10,9 +10,11 @@ #include "multisolver_interface/SolverFactory.h" std::vector available_solvers; -std::vector SolverLoader::GetAvailableSolvers() { +std::vector SolverLoader::GetAvailableSolvers( + std::shared_ptr logger) { if (available_solvers.empty()) { - if (LoadXpress::XpressIsCorrectlyInstalled()) { + LoadXpress::XpressLoader xpress_loader(logger); + if (xpress_loader.XpressIsCorrectlyInstalled(true)) { available_solvers.push_back(XPRESS_STR); } #ifdef COIN_OR @@ -23,8 +25,9 @@ std::vector SolverLoader::GetAvailableSolvers() { return available_solvers; } -SolverFactory::SolverFactory() - : _available_solvers(SolverLoader::GetAvailableSolvers()) { +SolverFactory::SolverFactory(std::shared_ptr logger) + : _available_solvers(SolverLoader::GetAvailableSolvers(logger)), + logger_(std::move(logger)) { isXpress_available_ = std::find(available_solvers.cbegin(), available_solvers.cend(), XPRESS_STR) != available_solvers.cend(); diff --git a/src/cpp/multisolver_interface/SolverXpress.cpp b/src/cpp/multisolver_interface/SolverXpress.cpp index cd0e4cf57..0938c3a47 100644 --- a/src/cpp/multisolver_interface/SolverXpress.cpp +++ b/src/cpp/multisolver_interface/SolverXpress.cpp @@ -8,7 +8,7 @@ #include "StringManip.h" using namespace LoadXpress; - +using namespace std::literals; /************************************************************************************************* ----------------------------------- Constructor/Desctructor -------------------------------- @@ -28,7 +28,8 @@ SolverXpress::SolverXpress() { std::lock_guard guard(license_guard); int status = 0; if (_NumberOfProblems == 0) { - initXpressEnv(); + LoadXpress::XpressLoader xpress_loader; + xpress_loader.initXpressEnv(); status = XPRSinit(NULL); zero_status_check(status, "initialize XPRESS environment", LOGLOCATION); } @@ -165,7 +166,7 @@ void SolverXpress::read_prob(const char *prob_name, const char *flags) { */ status = XPRSreadprob(_xprs, prob_name, flags); - zero_status_check(status, "read problem", LOGLOCATION); + zero_status_check(status, " read problem "s + prob_name, LOGLOCATION); // If param KEEPNROWS not -1 remove first row which is the objective function if (keeprows != -1) { diff --git a/src/cpp/multisolver_interface/environment.cc b/src/cpp/multisolver_interface/environment.cc index 926d59e84..8aae173bc 100644 --- a/src/cpp/multisolver_interface/environment.cc +++ b/src/cpp/multisolver_interface/environment.cc @@ -18,8 +18,8 @@ #include #include #include +#include #include - namespace LoadXpress { #define STRINGIFY2(X) #X @@ -137,7 +137,10 @@ std::function XPRSgetversion = nullptr; std::function XPRSgetintattrib = nullptr; -bool LoadXpressFunctions(DynamicLibrary* xpress_dynamic_library) { +XpressLoader::XpressLoader(std::shared_ptr logger) + : logger_(std::move(logger)) {} + +bool XpressLoader::LoadXpressFunctions(DynamicLibrary* xpress_dynamic_library) { // This was generated with the parse_header_xpress.py script. // See the comment at the top of the script. @@ -195,22 +198,25 @@ bool LoadXpressFunctions(DynamicLibrary* xpress_dynamic_library) { "exhaustive). [" + StringJoin(notFound) + "]. Please make sure that your XPRESS install is " - "up-to-date (>= 8.13.0)."); - std::cout << msg << std::endl; + "up-to-date (>= 8.13.0).\n"); + logger_->display_message(msg); return false; } return true; } -void printXpressBanner() { +void XpressLoader::printXpressBanner() { char banner[XPRS_MAXBANNERLENGTH]; XPRSgetbanner(banner); + std::ostringstream msg; - std::cout << "Xpress banner :\n" << banner << "\n"; + msg << "Xpress banner :\n" << banner << "\n"; + logger_->display_message(msg); } -std::string GetXpressVarFromEnvironmentVariables(const char* XPRESS_var, - bool verbose = true) { +std::string XpressLoader::GetXpressVarFromEnvironmentVariables( + const char* XPRESS_var, bool verbose) { + std::ostringstream msg; // Look for libraries pointed by XPRESSDIR first. std::string xpress_home_from_env = ""; #ifdef _MSC_VER @@ -225,8 +231,10 @@ std::string GetXpressVarFromEnvironmentVariables(const char* XPRESS_var, XPRESS_var); } else { if (verbose) { - std::cout << "[Windows getenv_s function]: " << XPRESS_var - << " doesn't exist!\n"; + msg.str(""); + msg << "[Windows getenv_s function]: " << XPRESS_var + << " doesn't exist!\n"; + logger_->display_message(msg); } } #else @@ -239,7 +247,8 @@ std::string GetXpressVarFromEnvironmentVariables(const char* XPRESS_var, return xpress_home_from_env; } -std::vector XpressDynamicLibraryPotentialPaths() { +std::vector XpressLoader::XpressDynamicLibraryPotentialPaths() { + std::ostringstream msg; std::vector potential_paths; const char* XPRESSDIR = "XPRESSDIR"; @@ -255,12 +264,15 @@ std::vector XpressDynamicLibraryPotentialPaths() { #elif defined(__GNUC__) // Linux potential_paths.push_back((prefix / "lib" / "libxprs.so").string()); #else - std::cout << "OS Not recognized by xpress/environment.cc." - << " You won't be able to use Xpress."; + msg << "OS Not recognized by xpress/environment.cc." + << " You won't be able to use Xpress."; + logger_->display_message(msg); #endif } else { - std::cout << "Warning: " - << "Environment variable " << XPRESSDIR << " undefined.\n"; + msg.str(""); + msg << "Warning: " + << "Environment variable " << XPRESSDIR << " undefined.\n"; + logger_->display_message(msg); } // Search for canonical places. @@ -272,13 +284,15 @@ std::vector XpressDynamicLibraryPotentialPaths() { #elif defined(__GNUC__) // Linux potential_paths.push_back("/opt/xpressmp/lib/libxprs.so"); #else - std::cout << "OS Not recognized by environment.cc." - << " You won't be able to use Xpress."; + msg.str(""); + msg << "OS Not recognized by environment.cc." + << " You won't be able to use Xpress."; + logger_->display_message(msg); #endif return potential_paths; } -bool LoadXpressDynamicLibrary(std::string& xpresspath) { +bool XpressLoader::LoadXpressDynamicLibrary(std::string& xpresspath) { static std::string xpress_lib_path; static std::once_flag xpress_loading_done; static bool ret; @@ -286,14 +300,16 @@ bool LoadXpressDynamicLibrary(std::string& xpresspath) { // static std::mutex mutex; // mutex.lock(); - - std::call_once(xpress_loading_done, []() { + std::ostringstream msg; + std::call_once(xpress_loading_done, [this, &msg]() { const std::vector canonical_paths = XpressDynamicLibraryPotentialPaths(); for (const std::string& path : canonical_paths) { + msg.str(""); if (xpress_library.TryToLoad(path)) { - std::cout << "Info: " - << "Found the Xpress library in " << path << ".\n"; + msg << "Info: " + << "Found the Xpress library in " << path << ".\n"; + logger_->display_message(msg); xpress_lib_path.clear(); std::filesystem::path p(path); // p.remove_filename(); @@ -305,10 +321,12 @@ bool LoadXpressDynamicLibrary(std::string& xpresspath) { if (xpress_library.LibraryIsLoaded()) { ret = LoadXpressFunctions(&xpress_library); } else { - std::string msg("Could not find the Xpress shared library. Looked in: [" + - StringJoin(canonical_paths) + - "]. Please check environment variable XPRESSDIR\n"); - std::cout << msg << std::endl; + msg.str(""); + msg << "Could not find the Xpress shared library. Looked in: [" + << StringJoin(canonical_paths) + << "]. Please check environment variable XPRESSDIR\n"; + msg << std::endl; + logger_->display_message(msg); ret = false; } }); @@ -317,7 +335,9 @@ bool LoadXpressDynamicLibrary(std::string& xpresspath) { return ret; } -int loadLicence(const std::string& lib_path, bool verbose) { +int XpressLoader::loadLicence(const std::string& lib_path, bool verbose) { + std::ostringstream msg; + //-----first let xpress find the licence int code = XPRSinit(nullptr); if (!code) { @@ -337,8 +357,9 @@ int loadLicence(const std::string& lib_path, bool verbose) { } } else { if (verbose) { - std::cout << "Warning: Environment variable " << XPAUTH_PATH - << " undefined.\n"; + msg.str(""); + msg << "Warning: Environment variable " << XPAUTH_PATH << " undefined.\n"; + logger_->display_message(msg); } } @@ -354,8 +375,9 @@ int loadLicence(const std::string& lib_path, bool verbose) { } } else { if (verbose) { - std::cout << "Warning: Environment variable " << XPRESS - << " undefined.\n"; + msg.str(""); + msg << "Warning: Environment variable " << XPRESS << " undefined.\n"; + logger_->display_message(msg); } } // --- in xpress bin dir @@ -366,7 +388,9 @@ int loadLicence(const std::string& lib_path, bool verbose) { } /** init XPRESS environment */ -bool initXpressEnv(bool verbose, int xpress_oem_license_key) { +bool XpressLoader::initXpressEnv(bool verbose, int xpress_oem_license_key) { + std::ostringstream msg; + std::string xpresspath; bool status = LoadXpressDynamicLibrary(xpresspath); if (!status) { @@ -378,8 +402,9 @@ bool initXpressEnv(bool verbose, int xpress_oem_license_key) { // if not an OEM key if (xpress_oem_license_key == 0) { if (verbose) { - std::cout << "Initialising xpress-MP with parameter " << xpresspath - << "\n"; + msg.str(""); + msg << "Initialising xpress-MP with parameter " << xpresspath << "\n"; + logger_->display_message(msg); } code = loadLicence(xpresspath, false); @@ -390,29 +415,36 @@ bool initXpressEnv(bool verbose, int xpress_oem_license_key) { printXpressBanner(); char version[16]; XPRSgetversion(version); - std::cout << "Warning: " - << "Optimizer version: " << version - << " (Antares-Xpansion was compiled with version " - << XPVERSION << ").\n"; + msg.str(""); + msg << "Warning: " + << "Optimizer version: " << version + << " (Antares-Xpansion was compiled with version " << XPVERSION + << ").\n"; + logger_->display_message(msg); } return true; } else { - std::cout << "XpressInterface: Xpress found at " << xpresspath << "\n"; + msg.str(""); + msg << "XpressInterface: Xpress found at " << xpresspath << "\n"; + char errmsg[256]; XPRSgetlicerrmsg(errmsg, 256); - std::cout << "Xpress License error : " << errmsg - << " (XPRSinit returned code " << code << "). Please check" - << " environment variable XPRESS.\n"; + msg << "Xpress License error : " << errmsg << " (XPRSinit returned code " + << code << "). Please check" + << " environment variable XPRESS.\n"; + logger_->display_message(msg); return false; } } else { // if OEM key if (verbose) { - std::cout << "Warning: " - << "Initialising xpress-MP with OEM key " - << xpress_oem_license_key << "\n"; + msg.str(""); + msg << "Warning: " + << "Initialising xpress-MP with OEM key " << xpress_oem_license_key + << "\n"; + logger_->display_message(msg); } int nvalue = 0; @@ -422,24 +454,32 @@ bool initXpressEnv(bool verbose, int xpress_oem_license_key) { XPRSlicense(&nvalue, slicmsg); if (verbose) { - std::cout << "First message from XPRSLicense : " << slicmsg << "\n"; + msg.str(""); + msg << "First message from XPRSLicense : " << slicmsg << "\n"; + logger_->display_message(msg); } nvalue = xpress_oem_license_key - ((nvalue * nvalue) / 19); ierr = XPRSlicense(&nvalue, slicmsg); if (verbose) { - std::cout << "Second message from XPRSLicense : " << slicmsg << "\n"; + msg.str(""); + msg << "Second message from XPRSLicense : " << slicmsg << "\n"; + logger_->display_message(msg); } if (ierr == 16) { if (verbose) { - std::cout << "Optimizer development software detected\n"; + msg.str(""); + msg << "Optimizer development software detected\n"; + logger_->display_message(msg); } } else if (ierr != 0) { // get the license error message XPRSgetlicerrmsg(errmsg, 256); - std::cout << "Xpress Error Message: " << errmsg << "\n"; + msg.str(""); + msg << "Xpress Error Message: " << errmsg << "\n"; + logger_->display_message(msg); return false; } @@ -448,14 +488,16 @@ bool initXpressEnv(bool verbose, int xpress_oem_license_key) { if (!code) { return true; } else { - std::cout << "XPRSinit returned code : " << code << "\n"; + msg.str(""); + msg << "XPRSinit returned code : " << code << "\n"; + logger_->display_message(msg); return false; } } } -bool XpressIsCorrectlyInstalled() { - bool correctlyInstalled = initXpressEnv(false); +bool XpressLoader::XpressIsCorrectlyInstalled(bool verbose) { + bool correctlyInstalled = initXpressEnv(verbose); if (correctlyInstalled) { XPRSfree(); } diff --git a/src/cpp/multisolver_interface/include/multisolver_interface/SolverFactory.h b/src/cpp/multisolver_interface/include/multisolver_interface/SolverFactory.h index 332eedb71..cfad21f28 100644 --- a/src/cpp/multisolver_interface/include/multisolver_interface/SolverFactory.h +++ b/src/cpp/multisolver_interface/include/multisolver_interface/SolverFactory.h @@ -2,8 +2,13 @@ #include +#include "ILogger.h" #include "multisolver_interface/SolverAbstract.h" +/** + * \enum mapper::SOLVER_TYPE + * \brief algo type + */ enum class SOLVER_TYPE { INTEGER, CONTINUOUS }; const std::string UNKNOWN_STR("UNKNOWN"), COIN_STR("COIN"), CBC_STR("CBC"), CLP_STR("CLP"), XPRESS_STR("XPRESS"); @@ -14,7 +19,8 @@ const std::string UNKNOWN_STR("UNKNOWN"), COIN_STR("COIN"), CBC_STR("CBC"), */ class SolverLoader { public: - static std::vector GetAvailableSolvers(); + static std::vector GetAvailableSolvers( + std::shared_ptr logger); }; /*! @@ -30,7 +36,8 @@ class SolverFactory { /** * @brief Constructor of SolverFactory, fills the list of available solvers */ - SolverFactory(); + explicit SolverFactory(std::shared_ptr logger = + std::make_shared()); public: /** @@ -72,4 +79,6 @@ class SolverFactory { const std::vector &get_solvers_list() const; bool isXpress_available_ = false; + + std::shared_ptr logger_; }; diff --git a/src/cpp/multisolver_interface/include/multisolver_interface/environment.h b/src/cpp/multisolver_interface/include/multisolver_interface/environment.h index e3c8b8452..022e8cfc5 100644 --- a/src/cpp/multisolver_interface/include/multisolver_interface/environment.h +++ b/src/cpp/multisolver_interface/include/multisolver_interface/environment.h @@ -16,6 +16,7 @@ #pragma once #include +#include "ILogger.h" #include "dynamic_library.h" extern "C" { typedef struct xo_prob_struct* XPRSprob; @@ -23,18 +24,44 @@ typedef struct xo_prob_struct* XPRSprob; namespace LoadXpress { -void printXpressBanner(); +/** + * \class XpressLoader + * @brief This class is the entry point to load xpress in runtime + */ +class XpressLoader { + public: + /** + * constructor, it must take default logger for legacy code + */ + explicit XpressLoader(std::shared_ptr logger = + std::make_shared()); + /** + * \brief intialiaze xpress env : load libs and check the licence + */ + bool initXpressEnv(bool verbose = false, int xpress_oem_license_key = 0); -bool initXpressEnv(bool verbose = true, int xpress_oem_license_key = 0); + /** + * \brief return true is Xpress is correctly installed (libs and licence + * found) + */ + bool XpressIsCorrectlyInstalled(bool verbose = false); -bool XpressIsCorrectlyInstalled(); -// clang-format off + private: + // clang-format off // Force the loading of the xpress dynamic library. It returns true if the // library was successfully loaded. This method can only be called once. // Successive calls are no-op. // // Note that it does not check if a token license can be grabbed. -bool LoadXpressDynamicLibrary(std::string &xpresspath); + bool LoadXpressDynamicLibrary(std::string &xpresspath); + void printXpressBanner(); + std::shared_ptr logger_; + std::vector XpressDynamicLibraryPotentialPaths(); + std::string GetXpressVarFromEnvironmentVariables(const char* XPRESS_var, + bool verbose = true); + bool LoadXpressFunctions(DynamicLibrary* xpress_dynamic_library); + int loadLicence(const std::string& lib_path, bool verbose); +}; // The list of #define and extern std::function<> below is generated directly // from xprs.h via parse_header_xpress.py diff --git a/src/cpp/xpansion_interfaces/ILogger.h b/src/cpp/xpansion_interfaces/ILogger.h index f9fbdfd58..325ce0ccb 100644 --- a/src/cpp/xpansion_interfaces/ILogger.h +++ b/src/cpp/xpansion_interfaces/ILogger.h @@ -18,6 +18,7 @@ enum class StoppingCriterion { absolute_gap, max_iteration }; + inline std::string criterion_to_str( const StoppingCriterion stopping_criterion) { std::string stop_crit(""); @@ -43,6 +44,7 @@ inline std::string criterion_to_str( } return stop_crit; } + struct LogData { double lb; double best_ub; @@ -65,20 +67,50 @@ struct LogData { int cumulative_number_of_subproblem_resolved; }; -struct ILoggerBenders { +/** + * \interface ILoggerXpansion + * \brief Xpansion Unique log Interface + */ +struct ILoggerXpansion { + /** + * \brief pure virtual method to display a std::string message + * \param str the message to be displayed + */ virtual void display_message(const std::string &str) = 0; + + /** + * display the underlying std::string in std::ostringstream + * \param str the message to be displayed + */ + void display_message(const std::ostringstream &msg) { + display_message(msg.str()); + } virtual void PrintIterationSeparatorBegin() = 0; virtual void PrintIterationSeparatorEnd() = 0; - virtual ~ILoggerBenders() = default; + virtual ~ILoggerXpansion() = default; }; -struct BendersLoggerBase : public ILoggerBenders { +/** + * useful for multi-proc run + */ +struct EmptyLogger : public ILoggerXpansion { + void display_message(const std::string &str) override {} + void PrintIterationSeparatorBegin() override {}; + void PrintIterationSeparatorEnd() override {}; + virtual ~EmptyLogger() {} + +}; + +/** + * this \class act like a log agregator + */ +struct BendersLoggerBase : public ILoggerXpansion { void display_message(const std::string &str) override { for (auto logger : loggers) { logger->display_message(str); } } - void AddLogger(std::shared_ptr logger) { + void AddLogger(std::shared_ptr logger) { loggers.push_back(logger); } @@ -95,9 +127,14 @@ struct BendersLoggerBase : public ILoggerBenders { } private: - std::vector> loggers; + std::vector> loggers; }; -class ILogger : public ILoggerBenders { + +/** + * \interface + * \brief abstract class for operational logs + */ +class ILogger : public ILoggerXpansion { public: virtual ~ILogger() = default; diff --git a/tests/cpp/ext_loop/ext_loop_test.cpp b/tests/cpp/ext_loop/ext_loop_test.cpp index 4b68c7f1f..050ac0c27 100644 --- a/tests/cpp/ext_loop/ext_loop_test.cpp +++ b/tests/cpp/ext_loop/ext_loop_test.cpp @@ -133,7 +133,8 @@ class MasterUpdateBaseTest : public ::testing::TestWithParam { auto solvers() { std::vector solvers_name; solvers_name.push_back("COIN"); - if (LoadXpress::XpressIsCorrectlyInstalled()) { + LoadXpress::XpressLoader xpress_loader; + if (xpress_loader.XpressIsCorrectlyInstalled()) { solvers_name.push_back("XPRESS"); } return solvers_name; diff --git a/tests/cpp/sensitivity/SensitivityStudyTest.cpp b/tests/cpp/sensitivity/SensitivityStudyTest.cpp index e19758083..1d605be00 100644 --- a/tests/cpp/sensitivity/SensitivityStudyTest.cpp +++ b/tests/cpp/sensitivity/SensitivityStudyTest.cpp @@ -107,7 +107,8 @@ class SensitivityStudyTest : public ::testing::Test { std::string mps_path, std::map> expec_output_data_map) { std::vector solvers_name = {coin_name}; - if (LoadXpress::XpressIsCorrectlyInstalled()) { + LoadXpress::XpressLoader xpress_loader; + if (xpress_loader.XpressIsCorrectlyInstalled()) { solvers_name.push_back(xpress_name); } From 3745d4c754eabcc75111c60a940d56c251e78ae9 Mon Sep 17 00:00:00 2001 From: abdoulbari zaher <32519851+a-zakir@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:05:10 +0200 Subject: [PATCH 7/9] Feature/embed mpi (#786) For what? portable xpansion on windows --- CMakeLists.txt | 27 +++++++++++--- .../Portable_Xpansion_Windows.md | 19 ++++++++++ src/python/antares_xpansion/benders_driver.py | 6 ++- .../antares_xpansion/config_file_parser.py | 30 +++++++-------- src/python/antares_xpansion/config_loader.py | 11 +++--- src/python/antares_xpansion/driver.py | 3 +- src/python/antares_xpansion/xpansionConfig.py | 3 ++ src/python/config.yaml.in | 1 + tests/python/test_benders_driver.py | 37 +++++++++++++------ 9 files changed, 96 insertions(+), 41 deletions(-) create mode 100644 conception/Architecture_decision_records/Portable_Xpansion_Windows.md diff --git a/CMakeLists.txt b/CMakeLists.txt index bde157b08..f9068548a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,6 +185,7 @@ if (MSVC) list(APPEND CMAKE_FIND_LIBRARY_PREFIXES "" "lib") endif () find_package(MPI REQUIRED) + find_package(Boost REQUIRED COMPONENTS mpi serialization) include_directories(${Boost_INCLUDE_DIRS}) @@ -312,12 +313,28 @@ if (USER_GUIDE_PATH) endif() install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/examples/" DESTINATION examples) - if (WIN32) - install(DIRECTORY ${CURRENT_RUNTIME_OUTPUT_DIRECTORY}/ - TYPE BIN - USE_SOURCE_PERMISSIONS - FILES_MATCHING PATTERN "*.dll") + cmake_path(GET MPIEXEC_EXECUTABLE PARENT_PATH MPI_BIN_DIR) + + #Copy associated .so and .dll to build directory + FILE(COPY ${MPI_BIN_DIR}/ + DESTINATION ${CURRENT_RUNTIME_OUTPUT_DIRECTORY} + USE_SOURCE_PERMISSIONS) + find_file(msmpi_dll NAMES msmpi.dll REGISTRY_VIEW HOST) + + install(DIRECTORY ${CURRENT_RUNTIME_OUTPUT_DIRECTORY}/ + TYPE BIN + USE_SOURCE_PERMISSIONS + FILES_MATCHING PATTERN "*.dll") + + install(FILES + ${MPI_msmpi_LIBRARY} + ${msmpi_dll} + DESTINATION bin + ) + + install(DIRECTORY ${MPI_BIN_DIR}/ + TYPE BIN) endif() install(DIRECTORY ${ANTARES_SOLVER_DIR}/ diff --git a/conception/Architecture_decision_records/Portable_Xpansion_Windows.md b/conception/Architecture_decision_records/Portable_Xpansion_Windows.md new file mode 100644 index 000000000..afb527e82 --- /dev/null +++ b/conception/Architecture_decision_records/Portable_Xpansion_Windows.md @@ -0,0 +1,19 @@ +# [Windows package] Export Mpi alongside Xpansion + + +## Status + +Accepted (2024/04/12) + +## Context + +Xpansion packages are not standalone, some dependencies must be pre-installed on the running machines ex MPI. + +## Decision + +The decision was taken to release a portable Xpansion Package on Windows only. +Such a thing is not desired and not even recommended on Linux dist, as Mpi rely on the host machines configuration. + +## Consequences + +A ready-to-use Xpansion package on Windows \ No newline at end of file diff --git a/src/python/antares_xpansion/benders_driver.py b/src/python/antares_xpansion/benders_driver.py index 47c74d1ce..6f1763cc5 100644 --- a/src/python/antares_xpansion/benders_driver.py +++ b/src/python/antares_xpansion/benders_driver.py @@ -13,12 +13,14 @@ class BendersDriver: - def __init__(self, benders, merge_mps, options_file) -> None: + + def __init__(self, benders, merge_mps, options_file, mpiexec=None) -> None: self.oversubscribe = False self.allow_run_as_root = False self.benders = benders self.merge_mps = merge_mps + self.mpiexec = mpiexec self.logger = step_logger(__name__, __class__.__name__) if (options_file != ""): @@ -132,7 +134,7 @@ def get_mpi_run_command_root(self): def _initialise_system_specific_mpi_vars(self): if sys.platform.startswith("win32"): - self.MPI_LAUNCHER = "mpiexec" + self.MPI_LAUNCHER = self.mpiexec elif sys.platform.startswith("linux"): self.MPI_LAUNCHER = "mpirun" else: diff --git a/src/python/antares_xpansion/config_file_parser.py b/src/python/antares_xpansion/config_file_parser.py index a39180ae2..e8d3eceda 100644 --- a/src/python/antares_xpansion/config_file_parser.py +++ b/src/python/antares_xpansion/config_file_parser.py @@ -1,4 +1,3 @@ - import yaml from antares_xpansion.xpansionConfig import ConfigParameters @@ -17,6 +16,7 @@ def __init__(self, config_file) -> None: self.FULL_RUN_DEFAULT = "full_run" self.ANTARES_ARCHIVE_UPDATER_DEFAULT = "antares_archive_updater" self.SENSITIVITY_DEFAULT = "sensitivity" + self.MPIEXEC_DEFAULT = "mpiexec" self.AVAILABLE_SOLVERS_DEFAULT = [] def get_config_parameters(self) -> ConfigParameters: @@ -27,21 +27,21 @@ def get_config_parameters(self) -> ConfigParameters: self.config = ConfigParameters( default_install_dir=content.get( - "DEFAULT_INSTALL_DIR", self.default_install_dir), - ANTARES=content.get('ANTARES', self.ANTARES_DEFAULT), - MERGE_MPS=content.get('MERGE_MPS', self.MERGE_MPS_DEFAULT), - BENDERS=content.get( - 'BENDERS', self.BENDERS_DEFAULT), - LP_NAMER=content.get('LP_NAMER', self.LP_NAMER_DEFAULT), - STUDY_UPDATER=content.get( - 'STUDY_UPDATER', self.STUDY_UPDATER_DEFAULT), - FULL_RUN=content.get( - 'FULL_RUN', self.FULL_RUN_DEFAULT), + "DEFAULT_INSTALL_DIR", self.default_install_dir + ), + ANTARES=content.get("ANTARES", self.ANTARES_DEFAULT), + MERGE_MPS=content.get("MERGE_MPS", self.MERGE_MPS_DEFAULT), + BENDERS=content.get("BENDERS", self.BENDERS_DEFAULT), + LP_NAMER=content.get("LP_NAMER", self.LP_NAMER_DEFAULT), + STUDY_UPDATER=content.get("STUDY_UPDATER", self.STUDY_UPDATER_DEFAULT), + FULL_RUN=content.get("FULL_RUN", self.FULL_RUN_DEFAULT), ANTARES_ARCHIVE_UPDATER=content.get( - 'ANTARES_ARCHIVE_UPDATER', self.ANTARES_ARCHIVE_UPDATER_DEFAULT), - SENSITIVITY_EXE=content.get( - 'SENSITIVITY', self.SENSITIVITY_DEFAULT), + "ANTARES_ARCHIVE_UPDATER", self.ANTARES_ARCHIVE_UPDATER_DEFAULT + ), + SENSITIVITY_EXE=content.get("SENSITIVITY", self.SENSITIVITY_DEFAULT), + MPIEXEC=content.get("mpiexec", self.MPIEXEC_DEFAULT), AVAILABLE_SOLVERS=content.get( - 'AVAILABLE_SOLVER', self.AVAILABLE_SOLVERS_DEFAULT) + "AVAILABLE_SOLVER", self.AVAILABLE_SOLVERS_DEFAULT + ), ) return self.config diff --git a/src/python/antares_xpansion/config_loader.py b/src/python/antares_xpansion/config_loader.py index 087c75f61..ed7ebf697 100644 --- a/src/python/antares_xpansion/config_loader.py +++ b/src/python/antares_xpansion/config_loader.py @@ -333,7 +333,6 @@ def get_batch_size(self): return int(batch_size_str) - def additional_constraints(self): """ returns path to additional constraints file @@ -507,7 +506,6 @@ def _set_last_simulation_name(self): self._set_xpansion_simulation_name() class NotAnXpansionOutputDir(Exception): pass - def _set_xpansion_simulation_name(self): xpansion_dir_suffix ="-Xpansion" @@ -530,7 +528,6 @@ def _set_xpansion_simulation_name(self): self._xpansion_simulation_name = self._last_study self._last_study =self._last_study.parent / (self._last_study.stem[:-len(xpansion_dir_suffix)]+".zip") - else: self._xpansion_simulation_name = self._last_study.parent / \ (self._last_study.stem+"-Xpansion") @@ -538,7 +535,7 @@ def _set_xpansion_simulation_name(self): def is_zip(self, study): _, ext = os.path.splitext(study) return ext == ".zip" - + def update_last_study_with_sensitivity_results(self): if self.is_zip(self._last_study): os.remove(self._last_study) @@ -563,7 +560,7 @@ def last_modified_study(self, root_dir:Path)-> Path: ) if len(sort_studies) == 0: raise ConfigLoader.MissingAntaresOutput("No Antares output is found") - + last_study = Path(root_dir) / sort_studies[-1] return last_study @@ -609,7 +606,6 @@ def lp_namer_exe(self): def benders_exe(self): return self.exe_path(self._config.BENDERS) - def merge_mps_exe(self): return self.exe_path(self._config.MERGE_MPS) @@ -720,3 +716,6 @@ class MissingAntaresOutput(Exception): def check_NTC_column_constraints(self, antares_version): checker = ChronicleChecker(self._config.data_dir, antares_version) checker.check_chronicle_constraints() + + def mpi_exe(self): + return self.exe_path(Path(self._config.MPIEXEC).name) diff --git a/src/python/antares_xpansion/driver.py b/src/python/antares_xpansion/driver.py index eedd5860e..fe8c0167c 100644 --- a/src/python/antares_xpansion/driver.py +++ b/src/python/antares_xpansion/driver.py @@ -48,7 +48,8 @@ def __init__(self, config_loader: ConfigLoader): self.benders_driver = BendersDriver( self.config_loader.benders_exe(), self.config_loader.merge_mps_exe(), - self.config_loader.options_file_name() + self.config_loader.options_file_name(), + self.config_loader.mpi_exe(), ) self.study_update_driver = StudyUpdaterDriver( diff --git a/src/python/antares_xpansion/xpansionConfig.py b/src/python/antares_xpansion/xpansionConfig.py index cf6d78e9a..81b5b5bf0 100644 --- a/src/python/antares_xpansion/xpansionConfig.py +++ b/src/python/antares_xpansion/xpansionConfig.py @@ -21,6 +21,7 @@ class ConfigParameters: SENSITIVITY_EXE: str FULL_RUN: str ANTARES_ARCHIVE_UPDATER: str + MPIEXEC: str AVAILABLE_SOLVERS: List[str] @@ -62,6 +63,7 @@ def __init__( self.ANTARES_ARCHIVE_UPDATER: str = "" self.MPI_LAUNCHER: str = "" self.MPI_N: str = "" + self.MPIEXEC: str = "" self.AVAILABLE_SOLVER: List[str] self._get_config_values() @@ -242,4 +244,5 @@ def _get_config_values(self): self.FULL_RUN = self.config_parameters.FULL_RUN self.ANTARES_ARCHIVE_UPDATER = self.config_parameters.ANTARES_ARCHIVE_UPDATER self.SENSITIVITY_EXE = self.config_parameters.SENSITIVITY_EXE + self.MPIEXEC = self.config_parameters.MPIEXEC self.AVAILABLE_SOLVER = self.config_parameters.AVAILABLE_SOLVERS diff --git a/src/python/config.yaml.in b/src/python/config.yaml.in index 722780fff..d37c9a549 100644 --- a/src/python/config.yaml.in +++ b/src/python/config.yaml.in @@ -7,5 +7,6 @@ STUDY_UPDATER : $ FULL_RUN : $ SENSITIVITY : $ ANTARES_ARCHIVE_UPDATER : $ +mpiexec : @MPIEXEC_EXECUTABLE@ AVAILABLE_SOLVER : @AVAILABLE_SOLVER_YML_LIST@ diff --git a/tests/python/test_benders_driver.py b/tests/python/test_benders_driver.py index cbfea10ea..3e88d0776 100644 --- a/tests/python/test_benders_driver.py +++ b/tests/python/test_benders_driver.py @@ -25,24 +25,24 @@ def setup_method(self): def test_lp_path(self, tmp_path): lp_path = tmp_path / "lp" os.mkdir(lp_path) - benders_driver = BendersDriver("", "", self.OPTIONS_JSON) + benders_driver = BendersDriver("", "", self.OPTIONS_JSON, self.MPI_LAUNCHER) benders_driver.set_simulation_output_path(tmp_path) assert benders_driver.get_lp_path() == lp_path def test_non_existing_output_path(self, tmp_path): - benders_driver = BendersDriver("", "", self.OPTIONS_JSON) + benders_driver = BendersDriver("", "", self.OPTIONS_JSON, self.MPI_LAUNCHER) with pytest.raises(BendersDriver.BendersOutputPathError): benders_driver.launch(tmp_path / "i_dont_exist", "test", False, 13) def test_empty_output_path(self, tmp_path): - benders_driver = BendersDriver("", "", self.OPTIONS_JSON) + benders_driver = BendersDriver("", "", self.OPTIONS_JSON, self.MPI_LAUNCHER) with pytest.raises(BendersDriver.BendersLpPathError): benders_driver.launch(tmp_path, "") def test_illegal_method(self, tmp_path): lp_path = tmp_path / "lp" os.mkdir(lp_path) - benders_driver = BendersDriver("", "", self.OPTIONS_JSON) + benders_driver = BendersDriver("", "", self.OPTIONS_JSON, self.MPI_LAUNCHER) with pytest.raises(BendersDriver.BendersSolverError): benders_driver.launch(tmp_path, "test") @@ -53,7 +53,9 @@ def test_benders_cmd_mpibenders(self, tmp_path): exe_path = os.path.normpath( os.path.join(my_install_dir, my_benders_mpi)) - benders_driver = BendersDriver(exe_path, "", self.OPTIONS_JSON) + benders_driver = BendersDriver( + exe_path, "", self.OPTIONS_JSON, self.MPI_LAUNCHER + ) simulation_output_path = tmp_path lp_path = Path(os.path.normpath( @@ -78,7 +80,8 @@ def test_benders_cmd_mpibenders_with_oversubscribe_linux_only(self, tmp_path): os.path.join(my_install_dir, my_benders_mpi)) benders_driver = BendersDriver( - exe_path, "", self.OPTIONS_JSON) + exe_path, "", self.OPTIONS_JSON, self.MPI_LAUNCHER + ) simulation_output_path = tmp_path lp_path = Path(os.path.normpath( @@ -100,7 +103,9 @@ def test_benders_cmd_sequential(self, tmp_path): exe_path = os.path.normpath( os.path.join(my_install_dir, my_sequential)) - benders_driver = BendersDriver(exe_path, "", self.OPTIONS_JSON) + benders_driver = BendersDriver( + exe_path, "", self.OPTIONS_JSON, self.MPI_LAUNCHER + ) simulation_output_path = tmp_path lp_path = Path(os.path.normpath( @@ -120,7 +125,9 @@ def test_benders_cmd_merge_mps(self, tmp_path): exe_path = os.path.normpath( os.path.join(my_install_dir, my_merges_mps)) - benders_driver = BendersDriver("", exe_path, self.OPTIONS_JSON) + benders_driver = BendersDriver( + "", exe_path, self.OPTIONS_JSON, self.MPI_LAUNCHER + ) simulation_output_path = tmp_path lp_path = Path(os.path.normpath( @@ -142,7 +149,9 @@ def test_raise_execution_error(self, tmp_path): exe_path = os.path.normpath( os.path.join(my_install_dir, my_benders_mpi)) - benders_driver = BendersDriver(exe_path, "", self.OPTIONS_JSON) + benders_driver = BendersDriver( + exe_path, "", self.OPTIONS_JSON, self.MPI_LAUNCHER + ) simulation_output_path = tmp_path lp_path = Path(os.path.normpath( @@ -161,7 +170,9 @@ def test_clean_solver_log_file(self, tmp_path): my_n_mpi = 13 exe_path = os.path.normpath( os.path.join(my_install_dir, my_benders_mpi)) - benders_driver = BendersDriver(exe_path, "", self.OPTIONS_JSON) + benders_driver = BendersDriver( + exe_path, "", self.OPTIONS_JSON, self.MPI_LAUNCHER + ) simulation_output_path = tmp_path lp_path = Path(os.path.normpath( @@ -191,7 +202,7 @@ def test_unsupported_platform(self, tmp_path): with patch(MOCK_SYS, autospec=True) as sys_: sys_.platform = "exotic_platform" with pytest.raises(BendersDriver.BendersUnsupportedPlatform): - BendersDriver("", "", self.OPTIONS_JSON) + BendersDriver("", "", self.OPTIONS_JSON, self.MPI_LAUNCHER) def test_clean_benders_step_if_not_keep_mps(self, tmp_path): my_benders_mpi = "something" @@ -200,7 +211,9 @@ def test_clean_benders_step_if_not_keep_mps(self, tmp_path): os.path.join(my_install_dir, my_benders_mpi)) keep_mps = False - benders_driver = BendersDriver(exe_path, "", self.OPTIONS_JSON) + benders_driver = BendersDriver( + exe_path, "", self.OPTIONS_JSON, self.MPI_LAUNCHER + ) simulation_output_path = tmp_path lp_path = Path(os.path.normpath( From 73cfe04d668a8a153625f136a9555f38765fc9c9 Mon Sep 17 00:00:00 2001 From: abdoulbari zaher <32519851+a-zakir@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:06:43 +0200 Subject: [PATCH 8/9] [ANT-1461] sensitivity study with unzipped outputs (#787) Get sensitivity study works with both zip/unzipped outputs --- src/python/antares_xpansion/config_loader.py | 15 ++++++++++----- src/python/antares_xpansion/full_run_driver.py | 2 -- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/python/antares_xpansion/config_loader.py b/src/python/antares_xpansion/config_loader.py index ed7ebf697..a3b2591d8 100644 --- a/src/python/antares_xpansion/config_loader.py +++ b/src/python/antares_xpansion/config_loader.py @@ -509,6 +509,8 @@ class NotAnXpansionOutputDir(Exception): def _set_xpansion_simulation_name(self): xpansion_dir_suffix ="-Xpansion" + self._xpansion_simulation_name = self._last_study + if self.step() in ["resume", "sensitivity"] : self._xpansion_simulation_name = self._last_study if self.is_zip(self._last_study): @@ -516,9 +518,10 @@ def _set_xpansion_simulation_name(self): with zipfile.ZipFile(self._last_study, 'r') as output_zip: output_zip.extractall(self._xpansion_simulation_name) elif self.step() == "benders": - if(not self._last_study.name.endswith(xpansion_dir_suffix)): - raise ConfigLoader.NotAnXpansionOutputDir(f"Error! {self._last_study} is not an Xpansion output directory") - self._xpansion_simulation_name = self._last_study + if self.is_zip(self._last_study): + raise ConfigLoader.NotAnXpansionOutputDir( + f"Error! {self._last_study} is not an Xpansion output directory" + ) elif self.step() == "problem_generation": if not self.is_zip(self._last_study): @@ -526,7 +529,9 @@ def _set_xpansion_simulation_name(self): raise ConfigLoader.NotAnXpansionOutputDir(f"Error! {self._last_study} is not an Xpansion output directory") else: self._xpansion_simulation_name = self._last_study - self._last_study =self._last_study.parent / (self._last_study.stem[:-len(xpansion_dir_suffix)]+".zip") + self._last_study = self._last_study.parent / ( + self._last_study.stem[: -len(xpansion_dir_suffix)] + ".zip" + ) else: self._xpansion_simulation_name = self._last_study.parent / \ @@ -545,7 +550,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' in study.name) + return ext == ".zip" or os.path.isdir(study) def last_modified_study(self, root_dir:Path)-> Path: list_dir = os.listdir(root_dir) diff --git a/src/python/antares_xpansion/full_run_driver.py b/src/python/antares_xpansion/full_run_driver.py index 7a978fd9f..77a4487ef 100644 --- a/src/python/antares_xpansion/full_run_driver.py +++ b/src/python/antares_xpansion/full_run_driver.py @@ -64,8 +64,6 @@ def run(self): os.chdir(lp_path) self.logger.info(f"Current directory is now: {os.getcwd()}") - self.logger.info(f"Command is {self.full_command()}") - print(self.full_command()) ret = subprocess.run( self.full_command(), shell=False, stdout=sys.stdout, stderr=sys.stderr, encoding='utf-8') From 0196dfc5af45d0156d7076326d078ad14f4551bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:33:28 +0200 Subject: [PATCH 9/9] Bump tj-actions/branch-names from 6 to 8 (#774) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [tj-actions/branch-names](https://github.com/tj-actions/branch-names) from 6 to 8.
Release notes

Sourced from tj-actions/branch-names's releases.

v8

Changes in v8.0.1

What's Changed

Full Changelog: https://github.com/tj-actions/branch-names/compare/v8...v8.0.1


Changes in v8.0.0

Major version to prevent errors with dependabot upgrades.

What's Changed

Full Changelog: https://github.com/tj-actions/branch-names/compare/v7...v8.0.0


v7.0.7

What's Changed

Full Changelog: https://github.com/tj-actions/branch-names/compare/v7...v7.0.7

v7.0.6

What's Changed

... (truncated)

Changelog

Sourced from tj-actions/branch-names's changelog.

🔄 Update

  • Updated README.md (331c645) - (jackton1)
  • Update README.md (1dd881b) - (Tonye Jack)
  • Update README.md (df61b49) - (Tonye Jack)

📝 Other

  • PR #282: update test.yml (6871f53) - (repo-ranger[bot])
  • PR #281: update tj-actions/verify-changed-files action to v18 (b08a65c) - (repo-ranger[bot])
  • PR #279: peter-evans/create-pull-request from 5 to 6 (daaa308) - (repo-ranger[bot])
  • PR #278: README.md (e6d7f60) - (repo-ranger[bot])
  • PR #277: update tj-actions/verify-changed-files action to v17 (6a42993) - (repo-ranger[bot])
  • PR #276: update github/codeql-action action to v3 (3480893) - (repo-ranger[bot])
  • PR #275: to v8 (ce11ce0) - (repo-ranger[bot])

⚙️ Miscellaneous Tasks

  • Update test.yml (fd631fd) - (Tonye Jack)
  • deps: Update tj-actions/verify-changed-files action to v18 (7a67835) - (renovate[bot])
  • deps: Update tj-actions/verify-changed-files action to v17 (2fc8b99) - (renovate[bot])
  • deps: Update github/codeql-action action to v3 (133b286) - (renovate[bot])

⬆️ Upgrades

  • Upgraded from v7.0.7 -> v8 (cf62a9f) - (jackton1)

8.0.0 - (2023-12-05)

➖ Remove

  • Deleted .github/workflows/auto-approve.yml (d412a8b) - (Tonye Jack)

📝 Other

⚙️ Miscellaneous Tasks

⬆️ Upgrades

  • Upgraded from v7.0.6 -> v7.0.7 (f78faab) - (jackton1)

7.0.7 - (2023-12-03)

... (truncated)

Commits
  • 6871f53 Merge pull request #282 from tj-actions/chore/update-test.yml
  • fd631fd chore: update test.yml
  • b08a65c Merge pull request #281 from tj-actions/renovate/tj-actions-verify-changed-fi...
  • 7a67835 chore(deps): update tj-actions/verify-changed-files action to v18
  • daaa308 Merge pull request #279 from tj-actions/dependabot/github_actions/peter-evans...
  • d7e1c12 Bump peter-evans/create-pull-request from 5 to 6
  • e6d7f60 Merge pull request #278 from tj-actions/chore/update-readme
  • 331c645 Updated README.md
  • 1dd881b Update README.md
  • 6a42993 Merge pull request #277 from tj-actions/renovate/tj-actions-verify-changed-fi...
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tj-actions/branch-names&package-manager=github_actions&previous-version=6&new-version=8)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jason Maréchal <45510813+JasonMarechal25@users.noreply.github.com> --- .github/workflows/centos-release.yml | 2 +- .github/workflows/centos7-system-deps-build.yml | 2 +- .github/workflows/ol8-release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/centos-release.yml b/.github/workflows/centos-release.yml index 8a6cfbef5..b6b684c10 100644 --- a/.github/workflows/centos-release.yml +++ b/.github/workflows/centos-release.yml @@ -127,7 +127,7 @@ jobs: singlefile_name: ${{ steps.zip_name.outputs.singlefile_name }} steps: - id: branch-name - uses: tj-actions/branch-names@v6 + uses: tj-actions/branch-names@v8 - name: Checkout uses: actions/checkout@v3 diff --git a/.github/workflows/centos7-system-deps-build.yml b/.github/workflows/centos7-system-deps-build.yml index 27ff365ed..1f58b290b 100644 --- a/.github/workflows/centos7-system-deps-build.yml +++ b/.github/workflows/centos7-system-deps-build.yml @@ -72,7 +72,7 @@ jobs: steps: - id: branch-name - uses: tj-actions/branch-names@v6 + uses: tj-actions/branch-names@v8 - uses: actions/checkout@v3 with: diff --git a/.github/workflows/ol8-release.yml b/.github/workflows/ol8-release.yml index 270c39129..15423c63d 100644 --- a/.github/workflows/ol8-release.yml +++ b/.github/workflows/ol8-release.yml @@ -98,7 +98,7 @@ jobs: singlefile_name: ${{ steps.zip_name.outputs.singlefile_name }} steps: - id: branch-name - uses: tj-actions/branch-names@v6 + uses: tj-actions/branch-names@v8 - name: Install System run: |