From 6ea957876ccdd48475bf64aa9cb3c42bee663679 Mon Sep 17 00:00:00 2001 From: Peter Mitri Date: Mon, 13 Jan 2025 14:57:04 +0100 Subject: [PATCH 1/6] Support multiple quadratic solvers Signed-off-by: Peter Mitri --- .../InfoCollection/StudyInfoCollector.cpp | 7 +- .../antares/optimization-options/options.h | 6 +- .../include/antares/study/load-options.h | 2 +- .../study/include/antares/study/parameters.h | 2 +- src/libs/antares/study/parameters.cpp | 13 ++-- src/solver/application/application.cpp | 6 +- .../include/antares/solver/misc/options.h | 2 +- src/solver/misc/options.cpp | 64 +++++++++++++++---- .../solver/optimisation/opt_fonctions.h | 2 +- .../opt_appel_solveur_lineaire.cpp | 4 +- .../optimisation/opt_optimisation_hebdo.cpp | 6 +- .../opt_pilotage_optimisation_quadratique.cpp | 9 ++- .../antares/solver/utils/ortools_utils.h | 30 +++++++-- src/solver/utils/ortools_utils.cpp | 50 ++++++++++----- .../end-to-end/simple_study/simple-study.cpp | 4 +- src/tests/src/api_internal/test_api.cpp | 44 +++++++------ .../study/parameters/parameters-tests.cpp | 9 ++- 17 files changed, 181 insertions(+), 79 deletions(-) diff --git a/src/libs/antares/InfoCollection/StudyInfoCollector.cpp b/src/libs/antares/InfoCollection/StudyInfoCollector.cpp index 7d07df9690..531f984ab5 100644 --- a/src/libs/antares/InfoCollection/StudyInfoCollector.cpp +++ b/src/libs/antares/InfoCollection/StudyInfoCollector.cpp @@ -145,8 +145,11 @@ void StudyInfoCollector::solverVersionToFileContent(FileContent& file_content) void StudyInfoCollector::ORToolsSolver(FileContent& file_content) { - std::string solverName = study_.parameters.optOptions.ortoolsSolver; - file_content.addItemToSection("study", "ortools solver", solverName); + std::string linearSolverName = study_.parameters.optOptions.linearSolver; + file_content.addItemToSection("study", "linear solver", linearSolverName); + + std::string quadraticSolverName = study_.parameters.optOptions.linearSolver; + file_content.addItemToSection("study", "quadratic solver", quadraticSolverName); } // Collecting data optimization problem diff --git a/src/libs/antares/optimization-options/include/antares/optimization-options/options.h b/src/libs/antares/optimization-options/include/antares/optimization-options/options.h index 19fea8b2e6..62aff96c36 100644 --- a/src/libs/antares/optimization-options/include/antares/optimization-options/options.h +++ b/src/libs/antares/optimization-options/include/antares/optimization-options/options.h @@ -28,8 +28,10 @@ namespace Antares::Solver::Optimization struct OptimizationOptions { //! The solver name, sirius is the default - std::string ortoolsSolver = "sirius"; + std::string linearSolver = "sirius"; + std::string quadraticSolver = "sirius"; + std::string linearSolverParameters; + std::string quadraticSolverParameters; bool solverLogs = false; - std::string solverParameters; }; } // namespace Antares::Solver::Optimization diff --git a/src/libs/antares/study/include/antares/study/load-options.h b/src/libs/antares/study/include/antares/study/load-options.h index 41eee86cbb..8a7353b63c 100644 --- a/src/libs/antares/study/include/antares/study/load-options.h +++ b/src/libs/antares/study/include/antares/study/load-options.h @@ -81,7 +81,7 @@ class StudyLoadOptions //! A non-zero value if the data will be used for a simulation bool usedByTheSolver; - //! All options related to optimization + //! All options related to linear & quadratic optimization Antares::Solver::Optimization::OptimizationOptions optOptions; //! Temporary string for passing log message diff --git a/src/libs/antares/study/include/antares/study/parameters.h b/src/libs/antares/study/include/antares/study/parameters.h index 664d574b32..c2a7fef593 100644 --- a/src/libs/antares/study/include/antares/study/parameters.h +++ b/src/libs/antares/study/include/antares/study/parameters.h @@ -503,7 +503,7 @@ class Parameters final // Naming constraints and variables in problems bool namedProblems; - // All options related to optimization + // All options related to linear & quadratic optimization Antares::Solver::Optimization::OptimizationOptions optOptions; private: diff --git a/src/libs/antares/study/parameters.cpp b/src/libs/antares/study/parameters.cpp index 60a3781bd2..d6846b9660 100644 --- a/src/libs/antares/study/parameters.cpp +++ b/src/libs/antares/study/parameters.cpp @@ -1283,8 +1283,10 @@ bool Parameters::loadFromINI(const IniFile& ini, const StudyVersion& version) void Parameters::handleOptimizationOptions(const StudyLoadOptions& options) { // Options only set from the command-line - optOptions.ortoolsSolver = options.optOptions.ortoolsSolver; - optOptions.solverParameters = options.optOptions.solverParameters; + optOptions.linearSolver = options.optOptions.linearSolver; + optOptions.linearSolverParameters = options.optOptions.linearSolverParameters; + optOptions.quadraticSolver = options.optOptions.quadraticSolver; + optOptions.quadraticSolverParameters = options.optOptions.quadraticSolverParameters; // Options that can be set both in command-line and file optOptions.solverLogs = options.optOptions.solverLogs || optOptions.solverLogs; @@ -1780,8 +1782,11 @@ void Parameters::prepareForSimulation(const StudyLoadOptions& options) logs.info() << " :: ignoring solution export"; } - logs.info() << " :: solver " << options.optOptions.ortoolsSolver - << " is used for problem resolution"; + logs.info() << " :: solver " << options.optOptions.linearSolver + << " is used for linear problem resolution"; + + logs.info() << " :: solver " << options.optOptions.quadraticSolver + << " is used for quadratic problem resolution"; // indicated that Problems will be named if (namedProblems) diff --git a/src/solver/application/application.cpp b/src/solver/application/application.cpp index 43dfa3368d..b3842c05ed 100644 --- a/src/solver/application/application.cpp +++ b/src/solver/application/application.cpp @@ -48,7 +48,9 @@ namespace { void printSolvers() { - std::cout << "Available solvers: " << availableOrToolsSolversString() << std::endl; + std::cout << "Available linear solvers: " << availableOrToolsSolversString(LINEAR) << std::endl; + std::cout << "Available quadratic solvers: " << availableOrToolsSolversString(QUADRATIC) + << std::endl; } } // namespace @@ -275,7 +277,7 @@ void Application::postParametersChecks() const { // Some more checks require the existence of pParameters, hence of a study. // Their execution is delayed up to this point. checkSolverMILPincompatibility(pParameters->unitCommitment.ucMode, - pParameters->optOptions.ortoolsSolver); + pParameters->optOptions.linearSolver); checkSimplexRangeHydroPricing(pParameters->simplexOptimizationRange, pParameters->hydroPricing.hpMode); diff --git a/src/solver/misc/include/antares/solver/misc/options.h b/src/solver/misc/include/antares/solver/misc/options.h index 00daf5aec7..1b0bc8f418 100644 --- a/src/solver/misc/include/antares/solver/misc/options.h +++ b/src/solver/misc/include/antares/solver/misc/options.h @@ -75,5 +75,5 @@ std::unique_ptr CreateParser(Settings& settings, void checkAndCorrectSettingsAndOptions(Settings& settings, Data::StudyLoadOptions& options); -void checkOrtoolsSolver(const Antares::Solver::Optimization::OptimizationOptions& optOptions); +void checkSolvers(Data::StudyLoadOptions& options); #endif /* __SOLVER_MISC_GETOPT_H__ */ diff --git a/src/solver/misc/options.cpp b/src/solver/misc/options.cpp index 00572039a9..f6b1216750 100644 --- a/src/solver/misc/options.cpp +++ b/src/solver/misc/options.cpp @@ -27,6 +27,8 @@ #include #include +#include + #include #include @@ -75,22 +77,53 @@ std::unique_ptr CreateParser(Settings& settings, StudyLoad "force-parallel", "Override the max number of years computed simultaneously"); + //--linear-solver + parser->add(options.optOptions.linearSolver, + ' ', + "linear-solver", + "Solver used for linear optimizations during simulation\nAvailable solver list : " + + availableOrToolsSolversString(LINEAR)); + //--solver - parser->add(options.optOptions.ortoolsSolver, + parser->add(options.optOptions.linearSolver, ' ', "solver", - "Solver used for simulation\nAvailable solver list : " - + availableOrToolsSolversString()); + "Deprecated, use linear-solver instead."); - //--solver-parameters + //--linear-solver-parameters parser->add( - options.optOptions.solverParameters, + options.optOptions.linearSolverParameters, ' ', - "solver-parameters", - "Set solver-specific parameters, for instance --solver-parameters=\"THREADS 1 PRESOLVE 1\"" + "linear-solver-parameters", + "Set linear solver-specific parameters, for instance --linear-solver-parameters=\"THREADS 1 " + "PRESOLVE 1\"" "for XPRESS or --solver-parameters=\"parallel/maxnthreads 1, lp/presolving TRUE\" for SCIP." "Syntax is solver-dependent, and only supported for SCIP & XPRESS."); + //--solver-parameters + parser->add(options.optOptions.linearSolverParameters, + ' ', + "solver-parameters", + "Deprecated, use linear-solver-parameters instead."); + + //--quadratic-solver + parser->add( + options.optOptions.quadraticSolver, + ' ', + "quadratic-solver", + "Solver used for quadratic optimizations during simulation\nAvailable solver list : " + + availableOrToolsSolversString(QUADRATIC)); + + //--quadratic-solver-parameters + parser->add( + options.optOptions.quadraticSolverParameters, + ' ', + "quadratic-solver-parameters", + "Set quadratic solver-specific parameters, for instance " + "--quadratic-solver-parameters=\"THREADS 1 PRESOLVE 1\"" + "for XPRESS or --solver-parameters=\"parallel/maxnthreads 1, lp/presolving TRUE\" for SCIP." + "Syntax is solver-dependent."); + parser->addParagraph("\nParameters"); // --name parser->add(settings.simulationName, @@ -246,7 +279,7 @@ void checkAndCorrectSettingsAndOptions(Settings& settings, Data::StudyLoadOption } options.checkForceSimulationMode(); - checkOrtoolsSolver(options.optOptions); + checkSolvers(options); // no-output and force-zip-output if (settings.noOutput && settings.forceZipOutput) @@ -255,19 +288,22 @@ void checkAndCorrectSettingsAndOptions(Settings& settings, Data::StudyLoadOption } } -void checkOrtoolsSolver(const Antares::Solver::Optimization::OptimizationOptions& optOptions) +void checkSolverExists(std::string solverName, const std::list availableSolversList) { - const std::string& solverName = optOptions.ortoolsSolver; - const std::list availableSolverList = getAvailableOrtoolsSolverName(); - // Check if solver is available - bool found = (std::ranges::find(availableSolverList, solverName) != availableSolverList.end()); + bool found = std::ranges::find(availableSolversList, solverName) != availableSolversList.end(); if (!found) { - throw Error::InvalidSolver(optOptions.ortoolsSolver, availableOrToolsSolversString()); + throw Error::InvalidSolver(solverName, boost::algorithm::join(availableSolversList, ",")); } } +void checkSolvers(StudyLoadOptions& options) +{ + checkSolverExists(options.optOptions.linearSolver, getAvailableOrtoolsMpSolverName()); + checkSolverExists(options.optOptions.quadraticSolver, getAvailableOrtoolsQuadraticSolverName()); +} + void Settings::checkAndSetStudyFolder(const std::string& folder) { // The study folder diff --git a/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h b/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h index 4d761bd558..f1631180ec 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h @@ -61,7 +61,7 @@ bool OPT_PilotageOptimisationLineaire(const OptimizationOptions& options, Solver::IResultWriter& writer, Solver::Simulation::ISimulationObserver& simulationObserver); void OPT_VerifierPresenceReserveJmoins1(PROBLEME_HEBDO*); -bool OPT_PilotageOptimisationQuadratique(PROBLEME_HEBDO*); +bool OPT_PilotageOptimisationQuadratique(const OptimizationOptions& options, PROBLEME_HEBDO*); /*! ** \brief Appel du solver diff --git a/src/solver/optimisation/opt_appel_solveur_lineaire.cpp b/src/solver/optimisation/opt_appel_solveur_lineaire.cpp index 304aab13df..760c25785a 100644 --- a/src/solver/optimisation/opt_appel_solveur_lineaire.cpp +++ b/src/solver/optimisation/opt_appel_solveur_lineaire.cpp @@ -190,7 +190,7 @@ static SimplexResult OPT_TryToCallSimplex(const OptimizationOptions& options, Probleme.NombreDeContraintesCoupes = 0; auto ortoolsProblem = std::make_unique(Probleme.isMIP(), - options.ortoolsSolver); + options.linearSolver); auto legacyOrtoolsFiller = std::make_unique(&Probleme); std::vector fillersCollection = {legacyOrtoolsFiller.get()}; LinearProblemData LP_Data; @@ -353,7 +353,7 @@ bool OPT_AppelDuSimplexe(const OptimizationOptions& options, Probleme.SetUseNamedProblems(true); auto ortoolsProblem = std::make_unique(Probleme.isMIP(), - options.ortoolsSolver); + options.linearSolver); auto legacyOrtoolsFiller = std::make_unique(&Probleme); std::vector fillersCollection = {legacyOrtoolsFiller.get()}; LinearProblemData LP_Data; diff --git a/src/solver/optimisation/opt_optimisation_hebdo.cpp b/src/solver/optimisation/opt_optimisation_hebdo.cpp index bd0d78d037..042f889151 100644 --- a/src/solver/optimisation/opt_optimisation_hebdo.cpp +++ b/src/solver/optimisation/opt_optimisation_hebdo.cpp @@ -27,13 +27,13 @@ using namespace Antares::Data; -using Antares::Solver::Optimization::OptimizationOptions; +using Solver::Optimization::OptimizationOptions; bool OPT_PilotageOptimisationLineaire(const OptimizationOptions&, PROBLEME_HEBDO*, Solver::IResultWriter&, Solver::Simulation::ISimulationObserver&); -bool OPT_PilotageOptimisationQuadratique(PROBLEME_HEBDO*); +bool OPT_PilotageOptimisationQuadratique(const OptimizationOptions&, PROBLEME_HEBDO*); void OPT_LiberationProblemesSimplexe(const PROBLEME_HEBDO*); void OPT_OptimisationHebdomadaire(const OptimizationOptions& options, @@ -52,7 +52,7 @@ void OPT_OptimisationHebdomadaire(const OptimizationOptions& options, else if (pProblemeHebdo->TypeDOptimisation == OPTIMISATION_QUADRATIQUE) { OPT_LiberationProblemesSimplexe(pProblemeHebdo); - if (!OPT_PilotageOptimisationQuadratique(pProblemeHebdo)) + if (!OPT_PilotageOptimisationQuadratique(options, pProblemeHebdo)) { logs.error() << "Quadratic optimization failed"; throw UnfeasibleProblemError("Quadratic optimization failed"); diff --git a/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp b/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp index 1337fb763b..0d13347416 100644 --- a/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp +++ b/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp @@ -23,8 +23,15 @@ #include "antares/solver/optimisation/constraints/constraint_builder_utils.h" #include "antares/solver/optimisation/opt_fonctions.h" -bool OPT_PilotageOptimisationQuadratique(PROBLEME_HEBDO* problemeHebdo) +bool OPT_PilotageOptimisationQuadratique(const OptimizationOptions& options, + PROBLEME_HEBDO* problemeHebdo) { + if (options.linearSolver != "sirius") + { + const std::string notFound = "Solver " + options.linearSolver + + " not supported for quadratic problems optimization."; + throw new std::invalid_argument(notFound); + } if (!problemeHebdo->LeProblemeADejaEteInstancie) { OPT_ConstruireLaListeDesVariablesOptimiseesDuProblemeQuadratique(problemeHebdo); diff --git a/src/solver/utils/include/antares/solver/utils/ortools_utils.h b/src/solver/utils/include/antares/solver/utils/ortools_utils.h index e5fae32c9d..24ea595a21 100644 --- a/src/solver/utils/include/antares/solver/utils/ortools_utils.h +++ b/src/solver/utils/include/antares/solver/utils/ortools_utils.h @@ -35,23 +35,43 @@ using namespace operations_research; +enum SolverClass +{ + LINEAR, + QUADRATIC +}; + void ORTOOLS_EcrireJeuDeDonneesLineaireAuFormatMPS(MPSolver* solver, Antares::Solver::IResultWriter& writer, const std::string& filename); /*! - * \brief Return list of available ortools solver name on our side + * \brief Return list of available ortools solver names on our side + * + * \return List of available ortools solver names + */ +std::list getAvailableOrtoolsSolverNames(SolverClass solverClass); + +/*! + * \brief Return list of available ortools linear solver names on our side + * + * \return List of available ortools linear solver names + */ +std::list getAvailableOrtoolsMpSolverName(); + +/*! + * \brief Return list of available ortools quadratic solver names on our side * - * \return List of available ortools solver name + * \return List of available ortools quadratic solver names */ -std::list getAvailableOrtoolsSolverName(); +std::list getAvailableOrtoolsQuadraticSolverName(); /*! * \brief Return a single string containing all solvers available, separated by a ", " and ending * with a ".". * */ -std::string availableOrToolsSolversString(); +std::string availableOrToolsSolversString(SolverClass solverClass); /*! * \brief Create a MPSolver with correct linear or mixed variant @@ -68,7 +88,7 @@ class OrtoolsUtils public: struct SolverNames { - std::string LPSolverName, MIPSolverName; + std::string LPSolverName, MIPSolverName, QuadraticSolverName; }; static const std::map solverMap; }; diff --git a/src/solver/utils/ortools_utils.cpp b/src/solver/utils/ortools_utils.cpp index c72fb67762..a040f87e63 100644 --- a/src/solver/utils/ortools_utils.cpp +++ b/src/solver/utils/ortools_utils.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include #include #include "antares/antares/Enum.hpp" @@ -236,7 +238,7 @@ MPSolver* ORTOOLS_Simplexe(Antares::Optimization::PROBLEME_SIMPLEXE_NOMME* Probl { solver->EnableOutput(); } - TuneSolverSpecificOptions(solver, options.ortoolsSolver, options.solverParameters); + TuneSolverSpecificOptions(solver, options.linearSolver, options.linearSolverParameters); const bool warmStart = solverSupportsWarmStart(solver->ProblemType()); // Provide an initial simplex basis, if any if (warmStart && Probleme->basisExists()) @@ -317,14 +319,23 @@ void ORTOOLS_LibererProbleme(MPSolver* solver) delete solver; } +// An empty name means that the solver does not support the optimization type +// NOTE: or-tools does not support calling SIRIUS to perform quadratic optimizations, we'll have to +// call SIRIUS explicitly, in a transparent way to the user const std::map OrtoolsUtils::solverMap = { - {"xpress", {"xpress_lp", "xpress"}}, - {"sirius", {"sirius_lp", "sirius"}}, - {"coin", {"clp", "cbc"}}, - {"glpk", {"glpk_lp", "glpk"}}, - {"scip", {"scip", "scip"}}}; + {"xpress", {"xpress_lp", "xpress", ""}}, + {"sirius", {"sirius_lp", "sirius", "sirius"}}, + {"coin", {"clp", "cbc", ""}}, + {"glpk", {"glpk_lp", "glpk", ""}}, + {"scip", {"scip", "scip", ""}}}; + +std::list getAvailableOrtoolsSolverNames(SolverClass solverClass) +{ + return solverClass == LINEAR ? getAvailableOrtoolsMpSolverName() + : getAvailableOrtoolsQuadraticSolverName(); +} -std::list getAvailableOrtoolsSolverName() +std::list getAvailableOrtoolsMpSolverName() { std::list result; @@ -341,17 +352,23 @@ std::list getAvailableOrtoolsSolverName() return result; } -std::string availableOrToolsSolversString() +std::list getAvailableOrtoolsQuadraticSolverName() { - const std::list availableSolverList = getAvailableOrtoolsSolverName(); - std::ostringstream solvers; - for (const std::string& avail: availableSolverList) + std::list result; + + for (const auto& solverName: OrtoolsUtils::solverMap) { - bool last = &avail == &availableSolverList.back(); - std::string sep = last ? "." : ", "; - solvers << avail << sep; + if (!solverName.second.QuadraticSolverName.empty()) + { + result.push_back(solverName.first); + } } - return solvers.str(); + return result; +} + +std::string availableOrToolsSolversString(SolverClass solverClass) +{ + return boost::algorithm::join(getAvailableOrtoolsSolverNames(solverClass), ",") + "."; } static std::optional translateSolverName(const std::string& solverName, bool isMip) @@ -375,7 +392,8 @@ static std::optional translateSolverName(const std::string& solverN MPSolver* MPSolverFactory(const bool isMip, const std::string& solverName) { - const std::string notFound = "Solver " + solverName + " not found"; + const std::string notFound = "Solver " + solverName + + " not supported for linear problems optimization."; const std::invalid_argument except(notFound); auto internalSolverName = translateSolverName(solverName, isMip); diff --git a/src/tests/end-to-end/simple_study/simple-study.cpp b/src/tests/end-to-end/simple_study/simple-study.cpp index dbc29c2ae4..dfff6c391d 100644 --- a/src/tests/end-to-end/simple_study/simple-study.cpp +++ b/src/tests/end-to-end/simple_study/simple-study.cpp @@ -201,7 +201,7 @@ BOOST_FIXTURE_TEST_CASE(milp_two_mc_single_unit_single_scenario, StudyFixture) // Use OR-Tools / COIN for MILP auto& p = study->parameters; p.unitCommitment.ucMode = ucMILP; - p.optOptions.ortoolsSolver = "coin"; + p.optOptions.linearSolver = "coin"; simulation->create(); simulation->run(); @@ -229,7 +229,7 @@ BOOST_FIXTURE_TEST_CASE(milp_two_mc_two_unit_single_scenario, StudyFixture) // Use OR-Tools / COIN for MILP auto& p = study->parameters; p.unitCommitment.ucMode = ucMILP; - p.optOptions.ortoolsSolver = "coin"; + p.optOptions.linearSolver = "coin"; simulation->create(); simulation->run(); diff --git a/src/tests/src/api_internal/test_api.cpp b/src/tests/src/api_internal/test_api.cpp index 47034cb19d..bb0897c278 100644 --- a/src/tests/src/api_internal/test_api.cpp +++ b/src/tests/src/api_internal/test_api.cpp @@ -32,7 +32,7 @@ #include "API.h" #include "in-memory-study.h" -class InMemoryStudyLoader: public Antares::IStudyLoader +class InMemoryStudyLoader: public IStudyLoader { public: explicit InMemoryStudyLoader(bool success = true): @@ -40,7 +40,7 @@ class InMemoryStudyLoader: public Antares::IStudyLoader { } - [[nodiscard]] std::unique_ptr load() const override + [[nodiscard]] std::unique_ptr load() const override { if (!success_) { @@ -60,7 +60,7 @@ class InMemoryStudyLoader: public Antares::IStudyLoader BOOST_AUTO_TEST_CASE(api_run_contains_antares_problem) { - Antares::API::APIInternal api; + API::APIInternal api; auto study_loader = std::make_unique(); auto results = api.run(*study_loader, {}, {}); @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE(api_run_contains_antares_problem) BOOST_AUTO_TEST_CASE(result_failure_when_study_is_null) { - Antares::API::APIInternal api; + API::APIInternal api; auto study_loader = std::make_unique(false); auto results = api.run(*study_loader, {}, {}); @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(result_failure_when_study_is_null) // Test where data in problems are consistant with data in study BOOST_AUTO_TEST_CASE(result_contains_problems) { - Antares::API::APIInternal api; + API::APIInternal api; auto study_loader = std::make_unique(); auto results = api.run(*study_loader, {}, {}); @@ -89,14 +89,16 @@ BOOST_AUTO_TEST_CASE(result_contains_problems) BOOST_CHECK_EQUAL(results.antares_problems.weeklyProblems.size(), 52); } -// Test where data in problems are consistant with data in study +// Test where data in problems are consistent with data in study BOOST_AUTO_TEST_CASE(result_with_ortools_coin) { - Antares::API::APIInternal api; + API::APIInternal api; auto study_loader = std::make_unique(); - const Antares::Solver::Optimization::OptimizationOptions opt{.ortoolsSolver = "coin", - .solverLogs = false, - .solverParameters = ""}; + const Solver::Optimization::OptimizationOptions opt{.linearSolver = "coin", + .quadraticSolver = "sirius", + .linearSolverParameters = "", + .quadraticSolverParameters = "", + .solverLogs = false}; auto results = api.run(*study_loader, {}, opt); @@ -106,17 +108,21 @@ BOOST_AUTO_TEST_CASE(result_with_ortools_coin) } // Test where we use an invalid OR-Tools solver -BOOST_AUTO_TEST_CASE(invalid_ortools_solver) +BOOST_AUTO_TEST_CASE(invalid_ortools_linear_solver) { - Antares::API::APIInternal api; + API::APIInternal api; auto study_loader = std::make_unique(); - const Antares::Solver::Optimization::OptimizationOptions opt{ - .ortoolsSolver = "this-solver-does-not-exist", - .solverLogs = true, - .solverParameters = ""}; + const Solver::Optimization::OptimizationOptions opt{ + .linearSolver = "this-solver-does-not-exist", + .quadraticSolver = "sirius", + .linearSolverParameters = "", + .quadraticSolverParameters = "", + .solverLogs = true}; auto shouldThrow = [&api, &study_loader, &opt] { return api.run(*study_loader, {}, opt); }; - BOOST_CHECK_EXCEPTION(shouldThrow(), - std::invalid_argument, - checkMessage("Solver this-solver-does-not-exist not found")); + BOOST_CHECK_EXCEPTION( + shouldThrow(), + std::invalid_argument, + checkMessage( + "Solver this-solver-does-not-exist not supported for linear problems optimization.")); } diff --git a/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp b/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp index 6015fcbbc0..72ee23e50e 100644 --- a/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp +++ b/src/tests/src/libs/antares/study/parameters/parameters-tests.cpp @@ -61,12 +61,14 @@ BOOST_FIXTURE_TEST_CASE(reset, Fixture) BOOST_CHECK_EQUAL(p.simulationDays.first, 0); BOOST_CHECK_EQUAL(p.nbTimeSeriesThermal, 1); BOOST_CHECK_EQUAL(p.synthesis, true); - BOOST_CHECK_EQUAL(p.optOptions.ortoolsSolver, "sirius"); + BOOST_CHECK_EQUAL(p.optOptions.linearSolver, "sirius"); + BOOST_CHECK_EQUAL(p.optOptions.quadraticSolver, "sirius"); } BOOST_FIXTURE_TEST_CASE(loadValid, Fixture) { - options.optOptions.ortoolsSolver = "xpress"; + options.optOptions.linearSolver = "xpress"; + options.optOptions.quadraticSolver = "scip"; writeValidFile(); p.loadFromFile(path.string(), version); @@ -76,7 +78,8 @@ BOOST_FIXTURE_TEST_CASE(loadValid, Fixture) BOOST_CHECK_EQUAL(p.nbYears, 5); BOOST_CHECK_EQUAL(p.seed[seedTsGenThermal], 5489); BOOST_CHECK_EQUAL(p.include.reserve.dayAhead, true); - BOOST_CHECK_EQUAL(p.optOptions.ortoolsSolver, "xpress"); + BOOST_CHECK_EQUAL(p.optOptions.linearSolver, "xpress"); + BOOST_CHECK_EQUAL(p.optOptions.quadraticSolver, "scip"); } BOOST_FIXTURE_TEST_CASE(fixBadValue, Fixture) From b5e422ec969e23ee5e8a89a3015b3da3d4426223 Mon Sep 17 00:00:00 2001 From: Peter Mitri Date: Mon, 13 Jan 2025 15:37:19 +0100 Subject: [PATCH 2/6] UI fix + doc Signed-off-by: Peter Mitri --- docs/user-guide/solver/02-command-line.md | 6 ++++-- .../solver/static-modeler/04-parameters.md | 20 ++++++++++++------- .../opt_pilotage_optimisation_quadratique.cpp | 4 ++-- src/ui/simulator/windows/simulation/run.cpp | 2 +- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/docs/user-guide/solver/02-command-line.md b/docs/user-guide/solver/02-command-line.md index 88defa9062..8334a3e9f5 100644 --- a/docs/user-guide/solver/02-command-line.md +++ b/docs/user-guide/solver/02-command-line.md @@ -17,7 +17,8 @@ hide: | --adequacy | Force the simulation in [adequacy](static-modeler/04-parameters.md#mode) mode | | --parallel | Enable [parallel](optional-features/multi-threading.md) computation of MC years | | --force-parallel=VALUE | Override the max number of years computed [simultaneously](optional-features/multi-threading.md) | -| --solver=VALUE | The optimization solver to use. Possible values are: `sirius` (default), `coin`, `xpress`, `scip` | +| --linear-solver=VALUE | The optimization solver to use for linear problems. Possible values are: `sirius` (default), `coin`, `xpress`, `scip` | +| --quadratic-solver=VALUE | The optimization solver to use for quadratic problems. Possible values are: `sirius` (default), `scip` | ## Parameters @@ -43,7 +44,8 @@ hide: | -m, --mps-export | Export anonymous MPS, weekly or daily optimal UC+dispatch linear (MPS will be named if the problem is infeasible) | | -s, --named-mps-problems | Export named MPS, weekly or daily optimal UC+dispatch linear | | --solver-logs | Print solver logs | -| --solver-parameters | Set solver-specific parameters, for instance `--solver-parameters="THREADS 1 PRESOLVE 1"` for XPRESS or `--solver-parameters="parallel/maxnthreads 1, lp/presolving TRUE"` for SCIP. Syntax is solver-dependent, and only supported for SCIP & XPRESS. | +| --linear-solver-parameters | Set solver-specific parameters for linear problems, for instance `--solver-parameters="THREADS 1 PRESOLVE 1"` for XPRESS or `--solver-parameters="parallel/maxnthreads 1, lp/presolving TRUE"` for SCIP. Syntax is solver-dependent, and only supported for SCIP & XPRESS. | +| --quadratic-solver-parameters | Set solver-specific parameters for quadratic problems. | ## Misc. diff --git a/docs/user-guide/solver/static-modeler/04-parameters.md b/docs/user-guide/solver/static-modeler/04-parameters.md index a50f982135..8e11763544 100644 --- a/docs/user-guide/solver/static-modeler/04-parameters.md +++ b/docs/user-guide/solver/static-modeler/04-parameters.md @@ -483,14 +483,20 @@ _**This section is under construction**_ > _**Note:**_ You can find more information on this parameter [here](../03-appendix.md#details-on-the-include-unfeasible-problem-behavior-parameter). --- -#### solver-parameters -[//]: # (TODO: document this parameter) -_**This section is under construction**_ +#### linear-solver-parameters +- **Expected value:** a string +- **Required:** **no** +- **Default value:** empty +- **Usage:** Set solver-specific parameters for linear problems, for instance `--solver-parameters="THREADS 1 PRESOLVE 1"` + for XPRESS or `--solver-parameters="parallel/maxnthreads 1, lp/presolving TRUE"` for SCIP. Syntax is solver-dependent, and only supported for SCIP & XPRESS. -- **Expected value:** -- **Required:** **yes** -- **Default value:** -- **Usage:** +--- +#### quadratic-solver-parameters +- **Expected value:** a string +- **Required:** **no** +- **Default value:** empty +- **Usage:** Set solver-specific parameters for quadratic problems, for instance `--solver-parameters="THREADS 1 PRESOLVE 1"` + for XPRESS or `--solver-parameters="parallel/maxnthreads 1, lp/presolving TRUE"` for SCIP. Syntax is solver-dependent, and only supported for SCIP & XPRESS. --- ## Adequacy-patch parameters diff --git a/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp b/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp index 0d13347416..93c4d2ca84 100644 --- a/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp +++ b/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp @@ -26,9 +26,9 @@ bool OPT_PilotageOptimisationQuadratique(const OptimizationOptions& options, PROBLEME_HEBDO* problemeHebdo) { - if (options.linearSolver != "sirius") + if (options.quadraticSolver != "sirius") { - const std::string notFound = "Solver " + options.linearSolver + const std::string notFound = "Solver " + options.quadraticSolver + " not supported for quadratic problems optimization."; throw new std::invalid_argument(notFound); } diff --git a/src/ui/simulator/windows/simulation/run.cpp b/src/ui/simulator/windows/simulation/run.cpp index 086a405e08..a44ed708bb 100644 --- a/src/ui/simulator/windows/simulation/run.cpp +++ b/src/ui/simulator/windows/simulation/run.cpp @@ -314,7 +314,7 @@ Run::Run(wxWindow* parent, bool preproOnly) : = Antares::Component::CreateLabel(pBigDaddy, wxT("Ortools solver : ")); pOrtoolsSolverCombox = new wxComboBox(pBigDaddy, wxID_ANY, "sirius"); - std::list solverList = getAvailableOrtoolsSolverName(); + std::list solverList = getAvailableOrtoolsSolverNames(LINEAR); for (const std::string& solverName : solverList) { pOrtoolsSolverCombox->Append(solverName); From 6511c37504ea6bf1029bf0258b05856fd4fadefe Mon Sep 17 00:00:00 2001 From: Peter Mitri Date: Thu, 16 Jan 2025 18:34:14 +0100 Subject: [PATCH 3/6] updates Signed-off-by: Peter Mitri --- src/solver/application/application.cpp | 7 +- src/solver/misc/options.cpp | 10 +- .../adq_patch_curtailment_sharing.cpp | 8 +- .../adequacy_patch_csr/solve_problem.cpp | 60 +++-- .../adequacy_patch_csr/solve_problem.h | 3 +- .../adequacy_patch_csr/hourly_csr_problem.h | 9 +- .../solver/optimisation/opt_fonctions.h | 4 +- .../opt_appel_solveur_quadratique.cpp | 46 +++- .../opt_pilotage_optimisation_quadratique.cpp | 10 +- .../optimisation/post_process_commands.cpp | 3 +- src/solver/simulation/economy.cpp | 5 +- .../solver/simulation/base_post_process.h | 7 +- .../antares/solver/simulation/solver.hxx | 1 + src/solver/utils/CMakeLists.txt | 19 +- .../solver/utils/ortools_quadratic_wrapper.h | 29 +++ .../antares/solver/utils/ortools_utils.h | 29 +-- .../utils/ortools_quadratic_wrapper.cpp | 211 ++++++++++++++++++ src/solver/utils/ortools_utils.cpp | 70 +++--- .../test_binding_constraints.cpp | 33 ++- .../resources/Antares_Simulator_Tests_NR | 2 +- src/ui/simulator/windows/simulation/run.cpp | 172 +++++++++----- 21 files changed, 573 insertions(+), 165 deletions(-) create mode 100644 src/solver/utils/include/antares/solver/utils/ortools_quadratic_wrapper.h create mode 100644 src/solver/utils/ortools_quadratic_wrapper.cpp diff --git a/src/solver/application/application.cpp b/src/solver/application/application.cpp index b3842c05ed..c01d1726ad 100644 --- a/src/solver/application/application.cpp +++ b/src/solver/application/application.cpp @@ -48,9 +48,10 @@ namespace { void printSolvers() { - std::cout << "Available linear solvers: " << availableOrToolsSolversString(LINEAR) << std::endl; - std::cout << "Available quadratic solvers: " << availableOrToolsSolversString(QUADRATIC) - << std::endl; + std::cout << "Available linear solvers: " // NOSONAR + << availableOrToolsSolversString(SolverClass::LINEAR) << std::endl; + std::cout << "Available quadratic solvers: " // NOSONAR + << availableOrToolsSolversString(SolverClass::QUADRATIC) << std::endl; } } // namespace diff --git a/src/solver/misc/options.cpp b/src/solver/misc/options.cpp index f6b1216750..b22e7f90b6 100644 --- a/src/solver/misc/options.cpp +++ b/src/solver/misc/options.cpp @@ -82,7 +82,7 @@ std::unique_ptr CreateParser(Settings& settings, StudyLoad ' ', "linear-solver", "Solver used for linear optimizations during simulation\nAvailable solver list : " - + availableOrToolsSolversString(LINEAR)); + + availableOrToolsSolversString(SolverClass::LINEAR)); //--solver parser->add(options.optOptions.linearSolver, @@ -112,7 +112,7 @@ std::unique_ptr CreateParser(Settings& settings, StudyLoad ' ', "quadratic-solver", "Solver used for quadratic optimizations during simulation\nAvailable solver list : " - + availableOrToolsSolversString(QUADRATIC)); + + availableOrToolsSolversString(SolverClass::QUADRATIC)); //--quadratic-solver-parameters parser->add( @@ -300,8 +300,10 @@ void checkSolverExists(std::string solverName, const std::list avai void checkSolvers(StudyLoadOptions& options) { - checkSolverExists(options.optOptions.linearSolver, getAvailableOrtoolsMpSolverName()); - checkSolverExists(options.optOptions.quadraticSolver, getAvailableOrtoolsQuadraticSolverName()); + checkSolverExists(options.optOptions.linearSolver, + getAvailableSolverNames(SolverClass::LINEAR)); + checkSolverExists(options.optOptions.quadraticSolver, + getAvailableSolverNames(SolverClass::QUADRATIC)); } void Settings::checkAndSetStudyFolder(const std::string& folder) diff --git a/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp b/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp index 731aaac79c..0742dad644 100644 --- a/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp +++ b/src/solver/optimisation/adequacy_patch_csr/adq_patch_curtailment_sharing.cpp @@ -198,12 +198,12 @@ void HourlyCSRProblem::setProblemCost() } } -void HourlyCSRProblem::solveProblem(uint week, int year) +void HourlyCSRProblem::solveProblem(uint week, int year, const OptimizationOptions& options) { - ADQ_PATCH_CSR(problemeAResoudre_, *this, adqPatchParams_, week, year); + ADQ_PATCH_CSR(options, problemeAResoudre_, *this, adqPatchParams_, week, year); } -void HourlyCSRProblem::run(uint week, uint year) +void HourlyCSRProblem::run(uint week, uint year, const OptimizationOptions& options) { calculateCsrParameters(); buildProblemVariables(); @@ -211,5 +211,5 @@ void HourlyCSRProblem::run(uint week, uint year) setVariableBounds(); buildProblemConstraintsRHS(); setProblemCost(); - solveProblem(week, year); + solveProblem(week, year, options); } diff --git a/src/solver/optimisation/adequacy_patch_csr/solve_problem.cpp b/src/solver/optimisation/adequacy_patch_csr/solve_problem.cpp index 4d209f1ade..93d5fb3799 100644 --- a/src/solver/optimisation/adequacy_patch_csr/solve_problem.cpp +++ b/src/solver/optimisation/adequacy_patch_csr/solve_problem.cpp @@ -31,6 +31,8 @@ */ #include +#include "antares/solver/utils/ortools_quadratic_wrapper.h" + extern "C" { #include "pi_define.h" @@ -142,7 +144,7 @@ void storeOrDisregardInteriorPointResults(const PROBLEME_ANTARES_A_RESOUDRE& Pro } } -double calculateCSRcost(const PROBLEME_POINT_INTERIEUR& Probleme, +double calculateCSRcost(const PROBLEME_ANTARES_A_RESOUDRE& Probleme, const HourlyCSRProblem& hourlyCsrProblem, const AdqPatchParams& adqPatchParams) { @@ -203,7 +205,7 @@ double calculateCSRcost(const PROBLEME_POINT_INTERIEUR& Probleme, return cost; } -void CSR_DEBUG_HANDLE(const PROBLEME_POINT_INTERIEUR& Probleme) +void CSR_DEBUG_HANDLE(PROBLEME_ANTARES_A_RESOUDRE& Probleme) { logs.info(); logs.info() << LOG_UI_DISPLAY_MESSAGES_OFF; @@ -238,7 +240,7 @@ void CSR_DEBUG_HANDLE(const PROBLEME_POINT_INTERIEUR& Probleme) } } -void handleInteriorPointError([[maybe_unused]] const PROBLEME_POINT_INTERIEUR& Probleme, +void handleInteriorPointError([[maybe_unused]] PROBLEME_ANTARES_A_RESOUDRE& Probleme, int hour, uint weekNb, int yearNb) @@ -253,23 +255,21 @@ void handleInteriorPointError([[maybe_unused]] const PROBLEME_POINT_INTERIEUR& P #endif } -bool ADQ_PATCH_CSR(PROBLEME_ANTARES_A_RESOUDRE& ProblemeAResoudre, +bool Solve(const OptimizationOptions& options, PROBLEME_ANTARES_A_RESOUDRE& ProblemeAResoudre); + +bool ADQ_PATCH_CSR(const OptimizationOptions& options, + PROBLEME_ANTARES_A_RESOUDRE& ProblemeAResoudre, HourlyCSRProblem& hourlyCsrProblem, const AdqPatchParams& adqPatchParams, uint weekNb, int yearNb) { - auto interiorPointProblem = buildInteriorPointProblem(ProblemeAResoudre); - double costPriorToCsr = calculateCSRcost(*interiorPointProblem, - hourlyCsrProblem, - adqPatchParams); - PI_Quamin(interiorPointProblem.get()); // resolution - if (interiorPointProblem->ExistenceDUneSolution == OUI_PI) + double costPriorToCsr = calculateCSRcost(ProblemeAResoudre, hourlyCsrProblem, adqPatchParams); + bool feasible = Solve(options, ProblemeAResoudre); + if (feasible) { setToZeroIfBelowThreshold(ProblemeAResoudre, hourlyCsrProblem); - double costAfterCsr = calculateCSRcost(*interiorPointProblem, - hourlyCsrProblem, - adqPatchParams); + double costAfterCsr = calculateCSRcost(ProblemeAResoudre, hourlyCsrProblem, adqPatchParams); storeOrDisregardInteriorPointResults(ProblemeAResoudre, hourlyCsrProblem, adqPatchParams, @@ -281,10 +281,36 @@ bool ADQ_PATCH_CSR(PROBLEME_ANTARES_A_RESOUDRE& ProblemeAResoudre, } else { - handleInteriorPointError(*interiorPointProblem, - hourlyCsrProblem.triggeredHour, - weekNb, - yearNb); + handleInteriorPointError(ProblemeAResoudre, hourlyCsrProblem.triggeredHour, weekNb, yearNb); return false; } } + +bool SolveWithSirius(const OptimizationOptions& options, + PROBLEME_ANTARES_A_RESOUDRE& ProblemeAResoudre) +{ + if (!options.quadraticSolverParameters.empty()) + { + logs.warning() + << "Quadratic solver parameters are not supported by SIRIUS; they will be ignored."; + } + auto interiorPointProblem = buildInteriorPointProblem(ProblemeAResoudre); + PI_Quamin(buildInteriorPointProblem(ProblemeAResoudre).get()); // resolution + return interiorPointProblem->ExistenceDUneSolution == OUI_PI; +} + +bool SolveWithOrtools(const OptimizationOptions& options, + PROBLEME_ANTARES_A_RESOUDRE& ProblemeAResoudre) +{ + SolveQuadraticProblemWithOrtools(options, &ProblemeAResoudre); + return ProblemeAResoudre.ExistenceDUneSolution == OUI_PI; +} + +bool Solve(const OptimizationOptions& options, PROBLEME_ANTARES_A_RESOUDRE& ProblemeAResoudre) +{ + if (options.quadraticSolver.compare("sirius") == 0) + { + return SolveWithSirius(options, ProblemeAResoudre); + } + return SolveWithOrtools(options, ProblemeAResoudre); +} diff --git a/src/solver/optimisation/adequacy_patch_csr/solve_problem.h b/src/solver/optimisation/adequacy_patch_csr/solve_problem.h index 59b0619ca2..bcd7bdd4a6 100644 --- a/src/solver/optimisation/adequacy_patch_csr/solve_problem.h +++ b/src/solver/optimisation/adequacy_patch_csr/solve_problem.h @@ -7,7 +7,8 @@ using namespace Antares::Data::AdequacyPatch; -bool ADQ_PATCH_CSR(PROBLEME_ANTARES_A_RESOUDRE&, +bool ADQ_PATCH_CSR(const OptimizationOptions& options, + PROBLEME_ANTARES_A_RESOUDRE&, HourlyCSRProblem&, const AdqPatchParams&, unsigned int week, diff --git a/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h b/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h index 6ebb9734e4..cbc751874f 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/adequacy_patch_csr/hourly_csr_problem.h @@ -27,6 +27,7 @@ #include #include +#include "antares/solver/optimisation/opt_fonctions.h" #include "antares/solver/optimisation/opt_structure_probleme_a_resoudre.h" #include "../variables/VariableManagerUtils.h" @@ -92,7 +93,9 @@ class HourlyCSRProblem triggeredHour = hour; } - void run(uint week, uint year); + void run(unsigned int week, + unsigned int year, + const Antares::Solver::Optimization::OptimizationOptions& options); private: void calculateCsrParameters(); @@ -102,7 +105,9 @@ class HourlyCSRProblem void buildProblemConstraintsLHS(); void buildProblemConstraintsRHS(); void setProblemCost(); - void solveProblem(uint week, int year); + void solveProblem(unsigned int week, + int year, + const Antares::Solver::Optimization::OptimizationOptions& options); void allocateProblem(); // variable construction diff --git a/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h b/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h index f1631180ec..51cd8302a1 100644 --- a/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h +++ b/src/solver/optimisation/include/antares/solver/optimisation/opt_fonctions.h @@ -54,7 +54,9 @@ void OPT_InitialiserLeSecondMembreDuProblemeLineaire(PROBLEME_HEBDO*, int, int, void OPT_InitialiserLeSecondMembreDuProblemeQuadratique(PROBLEME_HEBDO*, int); void OPT_InitialiserLesCoutsLineaire(PROBLEME_HEBDO*, const int, const int); void OPT_InitialiserLesCoutsQuadratiques(PROBLEME_HEBDO*, int); -bool OPT_AppelDuSolveurQuadratique(PROBLEME_ANTARES_A_RESOUDRE*, const int); +bool OPT_AppelDuSolveurQuadratique(const OptimizationOptions& options, + PROBLEME_ANTARES_A_RESOUDRE*, + const int); bool OPT_PilotageOptimisationLineaire(const OptimizationOptions& options, PROBLEME_HEBDO* problemeHebdo, diff --git a/src/solver/optimisation/opt_appel_solveur_quadratique.cpp b/src/solver/optimisation/opt_appel_solveur_quadratique.cpp index d59c6d8779..d570cc2a97 100644 --- a/src/solver/optimisation/opt_appel_solveur_quadratique.cpp +++ b/src/solver/optimisation/opt_appel_solveur_quadratique.cpp @@ -36,13 +36,44 @@ extern "C" } #include +#include "antares/optimization-options/options.h" #include "antares/solver/optimisation/opt_structure_probleme_a_resoudre.h" +#include "antares/solver/utils/ortools_quadratic_wrapper.h" +#include "antares/solver/utils/ortools_utils.h" using namespace Antares; -bool OPT_AppelDuSolveurQuadratique(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre, +void SolveWithSirius(const Solver::Optimization::OptimizationOptions& options, + PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre); +void ProcessResult(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre, int PdtHebdo); + +bool OPT_AppelDuSolveurQuadratique(const Solver::Optimization::OptimizationOptions& options, + PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre, const int PdtHebdo) { + // as long as sirius quadratic optimization is not supported through or-tools, we have to keep + // this code separate + if (options.quadraticSolver.compare("sirius") == 0) + { + SolveWithSirius(options, ProblemeAResoudre); + } + else + { + SolveQuadraticProblemWithOrtools(options, ProblemeAResoudre); + } + ProcessResult(ProblemeAResoudre, PdtHebdo); + return ProblemeAResoudre->ExistenceDUneSolution == OUI_PI; +} + +void SolveWithSirius(const Solver::Optimization::OptimizationOptions& options, + PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre) +{ + if (!options.quadraticSolverParameters.empty()) + { + logs.warning() + << "Quadratic solver parameters are not supported by SIRIUS; they will be ignored."; + } + PROBLEME_POINT_INTERIEUR Probleme; double ToleranceSurLAdmissibilite = 1.e-5; @@ -106,13 +137,9 @@ bool OPT_AppelDuSolveurQuadratique(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudr *pt = ProblemeAResoudre->X[i]; } } - - return true; } else { - logs.warning() << "Quadratic Optimisation: No solution, hour " << PdtHebdo; - for (int i = 0; i < ProblemeAResoudre->NombreDeVariables; i++) { double* pt = ProblemeAResoudre->AdresseOuPlacerLaValeurDesVariablesOptimisees[i]; @@ -121,7 +148,14 @@ bool OPT_AppelDuSolveurQuadratique(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudr *pt = std::numeric_limits::quiet_NaN(); } } + } +} +void ProcessResult(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre, const int PdtHebdo) +{ + if (ProblemeAResoudre->ExistenceDUneSolution == NON_PI) + { + logs.warning() << "Quadratic Optimisation: No solution, hour " << PdtHebdo; #ifndef NDEBUG { @@ -160,7 +194,5 @@ bool OPT_AppelDuSolveurQuadratique(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudr } } #endif - - return false; } } diff --git a/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp b/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp index 93c4d2ca84..5ff6e806e6 100644 --- a/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp +++ b/src/solver/optimisation/opt_pilotage_optimisation_quadratique.cpp @@ -26,12 +26,6 @@ bool OPT_PilotageOptimisationQuadratique(const OptimizationOptions& options, PROBLEME_HEBDO* problemeHebdo) { - if (options.quadraticSolver != "sirius") - { - const std::string notFound = "Solver " + options.quadraticSolver - + " not supported for quadratic problems optimization."; - throw new std::invalid_argument(notFound); - } if (!problemeHebdo->LeProblemeADejaEteInstancie) { OPT_ConstruireLaListeDesVariablesOptimiseesDuProblemeQuadratique(problemeHebdo); @@ -58,7 +52,9 @@ bool OPT_PilotageOptimisationQuadratique(const OptimizationOptions& options, OPT_InitialiserLesCoutsQuadratiques(problemeHebdo, pdtHebdo); - result = OPT_AppelDuSolveurQuadratique(problemeHebdo->ProblemeAResoudre.get(), pdtHebdo) + result = OPT_AppelDuSolveurQuadratique(options, + problemeHebdo->ProblemeAResoudre.get(), + pdtHebdo) && result; } } diff --git a/src/solver/optimisation/post_process_commands.cpp b/src/solver/optimisation/post_process_commands.cpp index 83446402a9..44723952e2 100644 --- a/src/solver/optimisation/post_process_commands.cpp +++ b/src/solver/optimisation/post_process_commands.cpp @@ -276,7 +276,8 @@ void CurtailmentSharingPostProcessCmd::execute(const optRuntimeData& opt_runtime logs.info() << "[adq-patch] CSR triggered for Year:" << year + 1 << " Hour:" << week * nbHoursInWeek + hourInWeek + 1; hourlyCsrProblem.setHour(hourInWeek); - hourlyCsrProblem.run(week, year); + auto opt = opt_runtime_data.options; + hourlyCsrProblem.run(week, year, opt); } } diff --git a/src/solver/simulation/economy.cpp b/src/solver/simulation/economy.cpp index 0b8b341148..eb62882f95 100644 --- a/src/solver/simulation/economy.cpp +++ b/src/solver/simulation/economy.cpp @@ -163,7 +163,10 @@ bool Economy::year(Progression::Task& progression, weeklyOptProblems_[numSpace].solve(); // Runs all the post processes in the list of post-process commands - optRuntimeData opt_runtime_data(state.year, w, hourInTheYear); + optRuntimeData opt_runtime_data(state.year, + w, + hourInTheYear, + study.parameters.optOptions); postProcessesList_[numSpace]->runAll(opt_runtime_data); variables.weekBegin(state); diff --git a/src/solver/simulation/include/antares/solver/simulation/base_post_process.h b/src/solver/simulation/include/antares/solver/simulation/base_post_process.h index f1e6215415..82f880c397 100644 --- a/src/solver/simulation/include/antares/solver/simulation/base_post_process.h +++ b/src/solver/simulation/include/antares/solver/simulation/base_post_process.h @@ -23,6 +23,7 @@ #include #include +#include "antares/solver/optimisation/opt_fonctions.h" #include "antares/solver/simulation/sim_structure_probleme_economique.h" using namespace Antares::Data; @@ -34,16 +35,18 @@ namespace Antares::Solver::Simulation struct optRuntimeData { - optRuntimeData(unsigned int y, unsigned int w, unsigned int h): + optRuntimeData(unsigned int y, unsigned int w, unsigned int h, const OptimizationOptions& opt): year(y), week(w), - hourInTheYear(h) + hourInTheYear(h), + options(opt) { } unsigned int year = 0; unsigned int week = 0; unsigned int hourInTheYear = 0; + const OptimizationOptions& options; }; class basePostProcessCommand diff --git a/src/solver/simulation/include/antares/solver/simulation/solver.hxx b/src/solver/simulation/include/antares/solver/simulation/solver.hxx index 7a47a75e69..add3dee009 100644 --- a/src/solver/simulation/include/antares/solver/simulation/solver.hxx +++ b/src/solver/simulation/include/antares/solver/simulation/solver.hxx @@ -30,6 +30,7 @@ #include "antares/concurrency/concurrency.h" #include "antares/solver/hydro/management/HydroInputsChecker.h" #include "antares/solver/hydro/management/management.h" +#include "antares/solver/optimisation/opt_fonctions.h" #include "antares/solver/simulation/opt_time_writer.h" #include "antares/solver/simulation/timeseries-numbers.h" #include "antares/solver/ts-generator/generator.h" diff --git a/src/solver/utils/CMakeLists.txt b/src/solver/utils/CMakeLists.txt index 16cf2a96e5..b1d58fc746 100644 --- a/src/solver/utils/CMakeLists.txt +++ b/src/solver/utils/CMakeLists.txt @@ -1,6 +1,8 @@ set(SRC include/antares/solver/utils/ortools_utils.h ortools_utils.cpp + include/antares/solver/utils/ortools_quadratic_wrapper.h + ortools_quadratic_wrapper.cpp include/antares/solver/utils/filename.h filename.cpp include/antares/solver/utils/named_problem.h @@ -11,10 +13,10 @@ set(SRC name_translator.cpp include/antares/solver/utils/opt_period_string_generator.h opt_period_string_generator.cpp - basis_status.cpp - include/antares/solver/utils/basis_status.h - basis_status_impl.cpp - basis_status_impl.h + basis_status.cpp + include/antares/solver/utils/basis_status.h + basis_status_impl.cpp + basis_status_impl.h ) add_library(utils ${SRC}) @@ -23,22 +25,19 @@ if (NOT MSVC) target_compile_options(utils PUBLIC "-Wno-unused-variable") -else () - #set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} /wd 4101") # unused local variable - #set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} /wd 4101") # unused local variable endif () target_link_libraries(utils PUBLIC ortools::ortools sirius_solver Antares::study - Antares::result_writer #ortools_utils.h + Antares::result_writer Antares::optimization-options - antares-core #enum.h + antares-core ) target_include_directories(utils - PUBLIC + PUBLIC $ ) diff --git a/src/solver/utils/include/antares/solver/utils/ortools_quadratic_wrapper.h b/src/solver/utils/include/antares/solver/utils/ortools_quadratic_wrapper.h new file mode 100644 index 0000000000..5147aa70b7 --- /dev/null +++ b/src/solver/utils/include/antares/solver/utils/ortools_quadratic_wrapper.h @@ -0,0 +1,29 @@ +/* +** Copyright 2007-2025, RTE (https://www.rte-france.com) +** See AUTHORS.txt +** SPDX-License-Identifier: MPL-2.0 +** This file is part of Antares-Simulator, +** Adequacy and Performance assessment for interconnected energy networks. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the Mozilla Public Licence 2.0 as published by +** the Mozilla Foundation, either version 2 of the License, or +** (at your option) any later version. +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** Mozilla Public Licence 2.0 for more details. +** +** You should have received a copy of the Mozilla Public Licence 2.0 +** along with Antares_Simulator. If not, see . +*/ + +#pragma once + +#include +#include + +void SolveQuadraticProblemWithOrtools( + const Antares::Solver::Optimization::OptimizationOptions& options, + PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre); diff --git a/src/solver/utils/include/antares/solver/utils/ortools_utils.h b/src/solver/utils/include/antares/solver/utils/ortools_utils.h index 24ea595a21..7b0c5b26d1 100644 --- a/src/solver/utils/include/antares/solver/utils/ortools_utils.h +++ b/src/solver/utils/include/antares/solver/utils/ortools_utils.h @@ -33,9 +33,14 @@ #include "ortools_wrapper.h" +namespace operations_research::math_opt +{ +enum class SolverType; +} + using namespace operations_research; -enum SolverClass +enum class SolverClass { LINEAR, QUADRATIC @@ -50,21 +55,7 @@ void ORTOOLS_EcrireJeuDeDonneesLineaireAuFormatMPS(MPSolver* solver, * * \return List of available ortools solver names */ -std::list getAvailableOrtoolsSolverNames(SolverClass solverClass); - -/*! - * \brief Return list of available ortools linear solver names on our side - * - * \return List of available ortools linear solver names - */ -std::list getAvailableOrtoolsMpSolverName(); - -/*! - * \brief Return list of available ortools quadratic solver names on our side - * - * \return List of available ortools quadratic solver names - */ -std::list getAvailableOrtoolsQuadraticSolverName(); +std::list getAvailableSolverNames(SolverClass solverClass); /*! * \brief Return a single string containing all solvers available, separated by a ", " and ending @@ -88,7 +79,9 @@ class OrtoolsUtils public: struct SolverNames { - std::string LPSolverName, MIPSolverName, QuadraticSolverName; + std::string LPSolverName, MIPSolverName; }; - static const std::map solverMap; + + static const std::map mpSolverMap; + static const std::map mathoptSolverMap; }; diff --git a/src/solver/utils/ortools_quadratic_wrapper.cpp b/src/solver/utils/ortools_quadratic_wrapper.cpp new file mode 100644 index 0000000000..0bfaa8152c --- /dev/null +++ b/src/solver/utils/ortools_quadratic_wrapper.cpp @@ -0,0 +1,211 @@ +/* +** Copyright 2007-2025, RTE (https://www.rte-france.com) +** See AUTHORS.txt +** SPDX-License-Identifier: MPL-2.0 +** This file is part of Antares-Simulator, +** Adequacy and Performance assessment for interconnected energy networks. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the Mozilla Public Licence 2.0 as published by +** the Mozilla Foundation, either version 2 of the License, or +** (at your option) any later version. +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** Mozilla Public Licence 2.0 for more details. +** +** You should have received a copy of the Mozilla Public Licence 2.0 +** along with Antares_Simulator. If not, see . +*/ + +#include +#include +#include + +#include +#include +#include + +/** + * This wrapper is an adapter that solves a QP stored in a PROBLEME_ANTARES_A_RESOUDRE + * using OR-Tools MathOpt API & solvers. + * Currently, QP support in MathOpt is implemented for Gurobi and SIP (though SCIP in OR-Tools + * has compilation issues), and under development for XPRESS. + */ + +using Antares::Solver::Optimization::OptimizationOptions; +using namespace operations_research::math_opt; + +constexpr double kInf = std::numeric_limits::infinity(); + +void BuildVariablesAndObjective(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre, Model& model); +void BuildConstraints(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre, Model& model); + +// TODO: interpret the following lines sent to sirius & adapt them to mathopt if needed +// Probleme->VariableBinaire = (char*)ProblemeAResoudre.CoutsReduits.data(); +// Probleme->UtiliserLaToleranceDAdmissibiliteParDefaut = OUI_PI; +// Probleme->UtiliserLaToleranceDeStationnariteParDefaut = OUI_PI; +// Probleme->UtiliserLaToleranceDeComplementariteParDefaut = OUI_PI; +// Probleme->CoutsMarginauxDesContraintes =ProblemeAResoudre.CoutsMarginauxDesContraintes.data() + +void checkOptions(const OptimizationOptions& options) +{ + auto availableSolversList = getAvailableSolverNames(SolverClass::QUADRATIC); + bool solverFound = std::ranges::find(availableSolversList, options.quadraticSolver) + != availableSolversList.end(); + if (!solverFound) + { + throw std::invalid_argument("Solver " + options.quadraticSolver + + " not supported for quadratic problems optimization."); + } + if (!options.quadraticSolverParameters.empty()) + { + // TODO: handle these by mapping them to generic or solver-specific params in mathopt + // TODO: or remove this for now? + Antares::logs.warning() + << "Quadratic solver parameters are not supported yet; they will be ignored."; + } +} + +void ProcessSolveResult(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre, + Model& model, + absl::StatusOr resultStatus); + +void SolveQuadraticProblemWithOrtools(const OptimizationOptions& options, + PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre) +{ + checkOptions(options); + Model model("Quadratic problem"); + BuildVariablesAndObjective(ProblemeAResoudre, model); + BuildConstraints(ProblemeAResoudre, model); + SolveArguments args; + args.parameters.enable_output = true; + if (options.solverLogs) + { + args.parameters.enable_output = true; + } + auto solverType = OrtoolsUtils::mathoptSolverMap.at(options.quadraticSolver); + auto resultStatus = Solve(model, solverType, args); + ProcessSolveResult(ProblemeAResoudre, model, resultStatus); +} + +void BuildVariablesAndObjective(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre, Model& model) +{ + QuadraticExpression objective(0); + for (auto i = 0; i < ProblemeAResoudre->NombreDeVariables; ++i) + { + double lb, ub; + switch (ProblemeAResoudre->TypeDeVariable[i]) + { + case VARIABLE_BORNEE_DES_DEUX_COTES: + lb = ProblemeAResoudre->Xmin[i]; + ub = ProblemeAResoudre->Xmax[i]; + break; + case VARIABLE_BORNEE_INFERIEUREMENT: + lb = ProblemeAResoudre->Xmin[i]; + ub = kInf; + break; + case VARIABLE_BORNEE_SUPERIEUREMENT: + lb = -kInf; + ub = ProblemeAResoudre->Xmax[i]; + break; + case VARIABLE_NON_BORNEE: + lb = -kInf; + ub = kInf; + break; + default: + throw std::invalid_argument("Unknown variable type: " + + std::to_string(ProblemeAResoudre->TypeDeVariable[i])); + } + std::string name = ProblemeAResoudre->NomDesVariables[i].empty() + ? "C" + std::to_string(i) + : ProblemeAResoudre->NomDesVariables[i]; + auto var = model.AddVariable(lb, ub, ProblemeAResoudre->VariablesEntieres[i], name); + objective += QuadraticExpression( + {QuadraticTerm(var, var, ProblemeAResoudre->CoutQuadratique[i])}, + {LinearTerm(var, ProblemeAResoudre->CoutLineaire[i])}, + 0.0); + } + model.SetObjective(objective, false); +} + +void BuildConstraints(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre, Model& model) +{ + for (auto iCt = 0; iCt < ProblemeAResoudre->NombreDeContraintes; ++iCt) + { + LinearExpression linear_expression(0); + for (auto iCoef = 0; iCoef < ProblemeAResoudre->NombreDeTermesDesLignes[iCt]; ++iCoef) + { + int iVar = ProblemeAResoudre->IndicesColonnes + .data()[ProblemeAResoudre->IndicesDebutDeLigne[iCt] + iCoef]; + auto coef = ProblemeAResoudre->CoefficientsDeLaMatriceDesContraintes + .data()[ProblemeAResoudre->IndicesDebutDeLigne[iCt] + iCoef]; + linear_expression += model.variable(iVar) * coef; + } + double lb = -kInf; + double ub = kInf; + switch (ProblemeAResoudre->Sens[iCt]) + { + case '=': + lb = ub = ProblemeAResoudre->SecondMembre[iCt]; + break; + case '<': + ub = ProblemeAResoudre->SecondMembre[iCt]; + break; + case '>': + lb = ProblemeAResoudre->SecondMembre[iCt]; + break; + default: + throw std::invalid_argument("Expected constraint sense to be =, <, or >, but was: " + + ProblemeAResoudre->Sens[iCt]); + } + BoundedLinearExpression bounded_linear_expression(std::move(linear_expression), lb, ub); + std::string name = ProblemeAResoudre->NomDesContraintes[iCt].empty() + ? "R" + std::to_string(iCt) + : ProblemeAResoudre->NomDesContraintes[iCt]; + model.AddLinearConstraint(bounded_linear_expression, name); + } +} + +void ProcessSolveResult(PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre, + Model& model, + absl::StatusOr resultStatus) +{ + if (resultStatus.ok() && resultStatus.value().has_primal_feasible_solution()) + { + ProblemeAResoudre->ExistenceDUneSolution = OUI_PI; + // Store result in problem structure + auto result = resultStatus.value(); + for (int i = 0; i < ProblemeAResoudre->NombreDeVariables; i++) + { + auto var = model.variable(i); + // Primal results + double value = result.best_primal_solution().variable_values.at(var); + ProblemeAResoudre->X[i] = value; + double* pt = ProblemeAResoudre->AdresseOuPlacerLaValeurDesVariablesOptimisees[i]; + if (pt) + { + *pt = value; + } + // Reduced costs + if (result.has_dual_feasible_solution()) + { + ProblemeAResoudre->CoutsReduits[i] = result.reduced_costs().at(var); + } + } + } + else + { + ProblemeAResoudre->ExistenceDUneSolution = NON_PI; + for (int i = 0; i < ProblemeAResoudre->NombreDeVariables; i++) + { + ProblemeAResoudre->X[i] = std::numeric_limits::quiet_NaN(); + double* pt = ProblemeAResoudre->AdresseOuPlacerLaValeurDesVariablesOptimisees[i]; + if (pt) + { + *pt = std::numeric_limits::quiet_NaN(); + } + } + } +} diff --git a/src/solver/utils/ortools_utils.cpp b/src/solver/utils/ortools_utils.cpp index a040f87e63..2985b1a926 100644 --- a/src/solver/utils/ortools_utils.cpp +++ b/src/solver/utils/ortools_utils.cpp @@ -21,7 +21,10 @@ #include "antares/solver/utils/ortools_utils.h" #include +#include +#include #include +#include #include @@ -210,8 +213,19 @@ void ORTOOLS_EcrireJeuDeDonneesLineaireAuFormatMPS(MPSolver* solver, removeTemporaryFile(tmpPath); } +static int iLp = 0; + bool solveAndManageStatus(MPSolver* solver, int& resultStatus, const MPSolverParameters& params) { + //++iLp; + // std::string filename = "/home/mitripet/debug_antares/lp_" + std::to_string(iLp) + ".lp"; + /*std::string lp; + solver->ExportModelAsLpFormat(false, &lp); + std::ofstream myfile; + myfile.open(filename); + myfile << lp; + myfile.close();*/ + // solver->Write(filename); auto status = solver->Solve(params); if (status == MPSolver::OPTIMAL || status == MPSolver::FEASIBLE) @@ -319,27 +333,22 @@ void ORTOOLS_LibererProbleme(MPSolver* solver) delete solver; } -// An empty name means that the solver does not support the optimization type -// NOTE: or-tools does not support calling SIRIUS to perform quadratic optimizations, we'll have to -// call SIRIUS explicitly, in a transparent way to the user -const std::map OrtoolsUtils::solverMap = { - {"xpress", {"xpress_lp", "xpress", ""}}, - {"sirius", {"sirius_lp", "sirius", "sirius"}}, - {"coin", {"clp", "cbc", ""}}, - {"glpk", {"glpk_lp", "glpk", ""}}, - {"scip", {"scip", "scip", ""}}}; - -std::list getAvailableOrtoolsSolverNames(SolverClass solverClass) -{ - return solverClass == LINEAR ? getAvailableOrtoolsMpSolverName() - : getAvailableOrtoolsQuadraticSolverName(); -} +const std::map OrtoolsUtils::mpSolverMap = { + {"xpress", {"xpress_lp", "xpress"}}, + {"sirius", {"sirius_lp", "sirius"}}, + {"coin", {"clp", "cbc"}}, + {"glpk", {"glpk_lp", "glpk"}}, + {"scip", {"scip", "scip"}}}; -std::list getAvailableOrtoolsMpSolverName() +const std::map OrtoolsUtils::mathoptSolverMap = { + {"scip", math_opt::SolverType::kGscip}, + {"xpress", math_opt::SolverType::kXpress}}; + +std::list getAvailableLinearSolverNames() { std::list result; - for (const auto& solverName: OrtoolsUtils::solverMap) + for (const auto& solverName: OrtoolsUtils::mpSolverMap) { MPSolver::OptimizationProblemType solverType; MPSolver::ParseSolverType(solverName.second.LPSolverName, &solverType); @@ -352,23 +361,30 @@ std::list getAvailableOrtoolsMpSolverName() return result; } -std::list getAvailableOrtoolsQuadraticSolverName() +std::list getAvailableQuadraticSolverNames() { std::list result; - - for (const auto& solverName: OrtoolsUtils::solverMap) + // Sirius is supported, but not through mathopt + result.push_back("sirius"); + for (const auto& solverName: OrtoolsUtils::mathoptSolverMap) { - if (!solverName.second.QuadraticSolverName.empty()) - { - result.push_back(solverName.first); - } + // TODO: check if solver supports quadratic objectives (doesn't seem possible through + // mathopt API, we must add hard-coded flags in antares). Not urgent, but we will need this + // when we will do linear optimization through mathopt also. + result.push_back(solverName.first); } return result; } +std::list getAvailableSolverNames(SolverClass solverClass) +{ + return solverClass == SolverClass::LINEAR ? getAvailableLinearSolverNames() + : getAvailableQuadraticSolverNames(); +} + std::string availableOrToolsSolversString(SolverClass solverClass) { - return boost::algorithm::join(getAvailableOrtoolsSolverNames(solverClass), ",") + "."; + return boost::algorithm::join(getAvailableSolverNames(solverClass), ",") + "."; } static std::optional translateSolverName(const std::string& solverName, bool isMip) @@ -377,11 +393,11 @@ static std::optional translateSolverName(const std::string& solverN { if (isMip) { - return OrtoolsUtils::solverMap.at(solverName).MIPSolverName; + return OrtoolsUtils::mpSolverMap.at(solverName).MIPSolverName; } else { - return OrtoolsUtils::solverMap.at(solverName).LPSolverName; + return OrtoolsUtils::mpSolverMap.at(solverName).LPSolverName; } } catch (const std::out_of_range&) diff --git a/src/tests/end-to-end/binding_constraints/test_binding_constraints.cpp b/src/tests/end-to-end/binding_constraints/test_binding_constraints.cpp index 10f91f2a40..fafc242892 100644 --- a/src/tests/end-to-end/binding_constraints/test_binding_constraints.cpp +++ b/src/tests/end-to-end/binding_constraints/test_binding_constraints.cpp @@ -38,6 +38,8 @@ struct StudyForBCTest: public StudyBuilder // Data members AreaLink* link = nullptr; + Area* area1 = nullptr; + Area* area2 = nullptr; std::shared_ptr cluster; std::shared_ptr BC; }; @@ -46,8 +48,8 @@ StudyForBCTest::StudyForBCTest() { simulationBetweenDays(0, 7); - Area* area1 = addAreaToStudy("Area 1"); - Area* area2 = addAreaToStudy("Area 2"); + area1 = addAreaToStudy("Area 1"); + area2 = addAreaToStudy("Area 2"); TimeSeriesConfigurer(area1->load.series.timeSeries).setColumnCount(1).fillColumnWith(0, 0); @@ -136,6 +138,15 @@ BOOST_FIXTURE_TEST_CASE(weekly_BC_restricts_link_direct_capacity_to_50, StudyWit OutputRetriever output(simulation->rawSimu()); unsigned int nbDaysInWeek = 7; BOOST_TEST(output.flow(link).week(0) == rhsValue * nbDaysInWeek, tt::tolerance(0.001)); + // TODO : revert these extra tests + BOOST_TEST(output.overallCost(area1).week(0) == 17500, tt::tolerance(0.001)); + BOOST_TEST(output.overallCost(area2).week(0) == 16450000, tt::tolerance(0.001)); + BOOST_TEST(output.load(area1).week(0) == 0, tt::tolerance(0.001)); + BOOST_TEST(output.load(area2).week(0) == 16800, tt::tolerance(0.001)); + BOOST_TEST(output.hydroStorage(area1).week(0) == 0, tt::tolerance(0.001)); + BOOST_TEST(output.hydroStorage(area2).week(0) == 0, tt::tolerance(0.001)); + BOOST_TEST(output.thermalGeneration(cluster.get()).week(0) == 350, tt::tolerance(0.001)); + BOOST_TEST(output.thermalNbUnitsON(cluster.get()).week(0) == 4, tt::tolerance(0.001)); } BOOST_FIXTURE_TEST_CASE(daily_BC_restricts_link_direct_capacity_to_60, StudyWithBConLink) @@ -239,6 +250,15 @@ BOOST_FIXTURE_TEST_CASE(On_year_2__RHS_TS_number_2_is_taken_into_account, StudyW OutputRetriever output(simulation->rawSimu()); BOOST_TEST(output.flow(link).hour(0) == bcGroupRHS2, tt::tolerance(0.001)); + // TODO : revert these extra tests + BOOST_TEST(output.overallCost(area1).week(0) == 588000, tt::tolerance(0.001)); + BOOST_TEST(output.overallCost(area2).week(0) == 5040000, tt::tolerance(0.001)); + BOOST_TEST(output.load(area1).week(0) == 0, tt::tolerance(0.001)); + BOOST_TEST(output.load(area2).week(0) == 16800, tt::tolerance(0.001)); + BOOST_TEST(output.hydroStorage(area1).week(0) == 0, tt::tolerance(0.001)); + BOOST_TEST(output.hydroStorage(area2).week(0) == 0, tt::tolerance(0.001)); + BOOST_TEST(output.thermalGeneration(cluster.get()).week(0) == 11760, tt::tolerance(0.001)); + BOOST_TEST(output.thermalNbUnitsON(cluster.get()).week(0) == 168, tt::tolerance(0.001)); } BOOST_FIXTURE_TEST_CASE(On_year_9__RHS_TS_number_4_is_taken_into_account, StudyWithBConLink) @@ -276,6 +296,15 @@ BOOST_FIXTURE_TEST_CASE(On_year_9__RHS_TS_number_4_is_taken_into_account, StudyW OutputRetriever output(simulation->rawSimu()); BOOST_TEST(output.flow(link).hour(0) == 40., tt::tolerance(0.001)); + // TODO : revert these extra tests + BOOST_TEST(output.overallCost(area1).week(0) == 336000, tt::tolerance(0.001)); + BOOST_TEST(output.overallCost(area2).week(0) == 10080000, tt::tolerance(0.001)); + BOOST_TEST(output.load(area1).week(0) == 0, tt::tolerance(0.001)); + BOOST_TEST(output.load(area2).week(0) == 16800, tt::tolerance(0.001)); + BOOST_TEST(output.hydroStorage(area1).week(0) == 0, tt::tolerance(0.001)); + BOOST_TEST(output.hydroStorage(area2).week(0) == 0, tt::tolerance(0.001)); + BOOST_TEST(output.thermalGeneration(cluster.get()).week(0) == 6720, tt::tolerance(0.001)); + BOOST_TEST(output.thermalNbUnitsON(cluster.get()).week(0) == 168, tt::tolerance(0.001)); } BOOST_FIXTURE_TEST_CASE(On_year_9__RHS_TS_number_4_out_of_bound_use_random_fallback_to_Oth_column, diff --git a/src/tests/resources/Antares_Simulator_Tests_NR b/src/tests/resources/Antares_Simulator_Tests_NR index 9983782bc0..8b65f36861 160000 --- a/src/tests/resources/Antares_Simulator_Tests_NR +++ b/src/tests/resources/Antares_Simulator_Tests_NR @@ -1 +1 @@ -Subproject commit 9983782bc07c62f99854b1c7394d320830a40e14 +Subproject commit 8b65f36861004a0ce5d9371735408c5934d4e65d diff --git a/src/ui/simulator/windows/simulation/run.cpp b/src/ui/simulator/windows/simulation/run.cpp index a44ed708bb..e0a46e95ff 100644 --- a/src/ui/simulator/windows/simulation/run.cpp +++ b/src/ui/simulator/windows/simulation/run.cpp @@ -20,37 +20,37 @@ */ #include "run.h" -#include -#include -#include -#include -#include -#include -#include +#include #include +#include #include #include #include +#include #include -#include -#include "../../toolbox/components/wizardheader.h" -#include "../../toolbox/components/button.h" -#include +#include +#include +#include +#include +#include -#include "../../toolbox/validator.h" -#include "../../toolbox/create.h" -#include "../../application/study.h" -#include "../../application/main/main.h" -#include "../../application/menus.h" -#include "../../windows/message.h" -#include "../../toolbox/system/diskfreespace.hxx" #include +#include +#include #include "antares/antares/Enum.hpp" #include "antares/solver/utils/ortools_utils.h" -#include +#include "../../application/main/main.h" +#include "../../application/menus.h" +#include "../../application/study.h" +#include "../../toolbox/components/button.h" +#include "../../toolbox/components/wizardheader.h" +#include "../../toolbox/create.h" +#include "../../toolbox/system/diskfreespace.hxx" +#include "../../toolbox/validator.h" +#include "../../windows/message.h" using namespace Yuni; @@ -69,22 +69,36 @@ static const Solver::Feature featuresAlias[featuresCount] = {Solver::standard, S static wxString TimeSeriesToWxString(uint m) { if (!m) + { return wxT("none"); + } wxString r; if (m & Data::timeSeriesLoad) + { r /*<< (r.empty() ? wxEmptyString : wxT(", "))*/ << wxT("load"); + } if (m & Data::timeSeriesHydro) + { r << (r.empty() ? wxEmptyString : wxT(", ")) << wxT("hydro"); + } if (m & Data::timeSeriesWind) + { r << (r.empty() ? wxEmptyString : wxT(", ")) << wxT("wind"); + } if (m & Data::timeSeriesThermal) + { r << (r.empty() ? wxEmptyString : wxT(", ")) << wxT("thermal"); + } if (r.empty()) + { return wxT("none"); + } else + { return r; + } } static inline void UpdateLabel(bool& guiUpdated, wxStaticText* label, const wxString& text) @@ -96,19 +110,21 @@ static inline void UpdateLabel(bool& guiUpdated, wxStaticText* label, const wxSt } } -class ResourcesInfoTimer final : public wxTimer +class ResourcesInfoTimer final: public wxTimer { public: - ResourcesInfoTimer(Run& form) : wxTimer(), pForm(form) + ResourcesInfoTimer(Run& form): + wxTimer(), + pForm(form) { } + virtual ~ResourcesInfoTimer() { } void Notify() override { - } private: @@ -127,8 +143,9 @@ void Run::gridAppend(wxFlexGridSizer& sizer, } else { - auto* t = Antares::Component::CreateLabel( - pBigDaddy, wxString(wxT(" ")) << title << wxT(" "), true); + auto* t = Antares::Component::CreateLabel(pBigDaddy, + wxString(wxT(" ")) << title << wxT(" "), + true); t->Enable(false); sizer.Add(t, 0, wxRIGHT | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL); } @@ -181,16 +198,16 @@ void Run::gridAppend(wxFlexGridSizer& sizer, const wxString& key, const wxString gridAppend(sizer, wxEmptyString, key, value); } -Run::Run(wxWindow* parent, bool preproOnly) : - wxDialog(parent, wxID_ANY, wxString(wxT("Simulation"))), - pSimulationName(nullptr), - pSimulationComments(nullptr), - pIgnoreWarnings(nullptr), - pTimer(nullptr), - pWarnAboutMemoryLimit(false), - pWarnAboutDiskLimit(false), - pAlreadyWarnedNoMCYear(false), - pFeatureIndex(0) +Run::Run(wxWindow* parent, bool preproOnly): + wxDialog(parent, wxID_ANY, wxString(wxT("Simulation"))), + pSimulationName(nullptr), + pSimulationComments(nullptr), + pIgnoreWarnings(nullptr), + pTimer(nullptr), + pWarnAboutMemoryLimit(false), + pWarnAboutDiskLimit(false), + pAlreadyWarnedNoMCYear(false), + pFeatureIndex(0) { assert(parent); @@ -223,8 +240,8 @@ Run::Run(wxWindow* parent, bool preproOnly) : // Informations about the INPUT { - auto* lblMode - = Antares::Component::CreateLabel(pBigDaddy, wxStringFromUTF8(study.header.caption)); + auto* lblMode = Antares::Component::CreateLabel(pBigDaddy, + wxStringFromUTF8(study.header.caption)); wxFont f = lblMode->GetFont(); f.SetWeight(wxFONTWEIGHT_BOLD); lblMode->SetFont(f); @@ -232,7 +249,8 @@ Run::Run(wxWindow* parent, bool preproOnly) : } { auto* lblMode = Antares::Component::CreateLabel( - pBigDaddy, wxStringFromUTF8(Data::SimulationModeToCString(study.parameters.mode))); + pBigDaddy, + wxStringFromUTF8(Data::SimulationModeToCString(study.parameters.mode))); wxFont f = lblMode->GetFont(); f.SetWeight(wxFONTWEIGHT_BOLD); lblMode->SetFont(f); @@ -278,8 +296,9 @@ Run::Run(wxWindow* parent, bool preproOnly) : s->AddSpacer(2); #endif - pPreproOnly = new wxCheckBox( - pBigDaddy, wxID_ANY, wxString(wxT(" Run the time-series generators only "))); + pPreproOnly = new wxCheckBox(pBigDaddy, + wxID_ANY, + wxString(wxT(" Run the time-series generators only "))); pPreproOnly->SetValue(preproOnly); pPreproOnly->Connect(pPreproOnly->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED, @@ -310,12 +329,12 @@ Run::Run(wxWindow* parent, bool preproOnly) : // Ortools use { // Ortools solver selection - pTitleOrtoolsSolverCombox - = Antares::Component::CreateLabel(pBigDaddy, wxT("Ortools solver : ")); + pTitleOrtoolsSolverCombox = Antares::Component::CreateLabel(pBigDaddy, + wxT("Ortools solver : ")); pOrtoolsSolverCombox = new wxComboBox(pBigDaddy, wxID_ANY, "sirius"); - std::list solverList = getAvailableOrtoolsSolverNames(LINEAR); - for (const std::string& solverName : solverList) + std::list solverList = getAvailableOrtoolsSolverNames(SolverClass::LINEAR); + for (const std::string& solverName: solverList) { pOrtoolsSolverCombox->Append(solverName); } @@ -352,8 +371,10 @@ Run::Run(wxWindow* parent, bool preproOnly) : sizer = new wxBoxSizer(wxHORIZONTAL); title = Antares::Component::CreateLabel(pBigDaddy, wxT("Memory (estimation) : ")); pLblEstimation = Antares::Component::CreateLabel(pBigDaddy, wxEmptyString); - pLblEstimationAvailable - = Antares::Component::CreateLabel(pBigDaddy, wxEmptyString, false, true); + pLblEstimationAvailable = Antares::Component::CreateLabel(pBigDaddy, + wxEmptyString, + false, + true); sizer->Add(pLblEstimation, 0, wxALL | wxEXPAND); sizer->AddSpacer(10); sizer->Add(pLblEstimationAvailable, 0, wxALL | wxEXPAND); @@ -362,8 +383,10 @@ Run::Run(wxWindow* parent, bool preproOnly) : sizer = new wxBoxSizer(wxHORIZONTAL); title = Antares::Component::CreateLabel(pBigDaddy, wxT("Disk (estimation) : ")); pLblDiskEstimation = Antares::Component::CreateLabel(pBigDaddy, wxEmptyString); - pLblDiskEstimationAvailable - = Antares::Component::CreateLabel(pBigDaddy, wxEmptyString, false, true); + pLblDiskEstimationAvailable = Antares::Component::CreateLabel(pBigDaddy, + wxEmptyString, + false, + true); sizer->Add(pLblDiskEstimation, 0, wxALL | wxEXPAND); sizer->AddSpacer(10); sizer->Add(pLblDiskEstimationAvailable, 0, wxALL | wxEXPAND); @@ -413,9 +436,13 @@ Run::Run(wxWindow* parent, bool preproOnly) : Run::~Run() { if (pNbCores) + { delete pNbCores; + } if (pTitleSimCores) + { delete pTitleSimCores; + } if (pTimer) { @@ -456,11 +483,15 @@ bool Run::createCommentsFile(String& filename) const filename.clear(); if (not pSimulationComments) + { return false; + } wxString cmt = pSimulationComments->GetValue(); if (cmt.empty()) + { return false; + } String content; wxStringToString(cmt, content); @@ -472,7 +503,9 @@ bool Run::createCommentsFile(String& filename) const String temporary; if (not IO::Directory::System::Temporary(temporary)) + { return false; + } auto processID = ProcessID(); @@ -500,7 +533,9 @@ int Run::checkForLowResources() #endif if (not pWarnAboutMemoryLimit and not pWarnAboutDiskLimit) + { return 0; + } auto& mainFrm = *Forms::ApplWnd::Instance(); @@ -508,16 +543,25 @@ int Run::checkForLowResources() if (pWarnAboutMemoryLimit) { if (pWarnAboutDiskLimit) + { msg = wxT("Memory and Disk"); + } else + { msg = wxT("Memory"); + } } else + { msg = wxT("Disk"); + } msg << wxT(" almost full"); - Window::Message message( - &mainFrm, wxT("Simulation"), msg, wxT("Try anyway ?"), "images/misc/warning.png"); + Window::Message message(&mainFrm, + wxT("Simulation"), + msg, + wxT("Try anyway ?"), + "images/misc/warning.png"); message.add(Window::Message::btnContinue); message.add(Window::Message::btnCancel, true); if (message.showModal() != Window::Message::btnContinue) @@ -531,7 +575,9 @@ int Run::checkForLowResources() void Run::onRun(void*) { if (not CurrentStudyIsValid()) + { return; + } bool canNotifyUserForLowResources = true; switch (checkForLowResources()) @@ -568,13 +614,19 @@ void Run::onRun(void*) } if (canNotifyUserForLowResources and 1 == checkForLowResources()) + { return; + } // Updating the display if (pTimer) + { pTimer->Stop(); + } if (!(!pThread)) + { pThread->stop(); + } this->Enable(false); Refresh(); @@ -605,9 +657,13 @@ void Run::onRun(void*) String commentFile; if (not createCommentsFile(commentFile)) + { commentFile.clear(); + } else + { logs.debug() << "using comment file: " << commentFile; + } Hide(); @@ -701,7 +757,9 @@ void Run::updateNbCores() wxString s = wxT(""); s << nbCoresRaw; if (minNbCores < nbCoresRaw) + { s << L" (smallest batch size : " << minNbCores << L")"; + } pNbCores->SetLabel(s); pBtnRun->Enable(true); @@ -734,12 +792,12 @@ void Run::prepareMenuSolverMode(Antares::Component::Button&, wxMenu& menu, void* for (uint i = 0; i != featuresCount; ++i) { const wxMenuItem* it = Menu::CreateItem(&menu, - wxID_ANY, - featuresNames[i], - "images/16x16/empty.png", - wxEmptyString, - wxITEM_NORMAL, - (i == 0)); + wxID_ANY, + featuresNames[i], + "images/16x16/empty.png", + wxEmptyString, + wxITEM_NORMAL, + (i == 0)); pMappingSolverMode[it->GetId()] = i; menu.Connect(it->GetId(), @@ -754,7 +812,9 @@ void Run::onSelectMode(wxCommandEvent& evt) { pFeatureIndex = pMappingSolverMode[evt.GetId()]; if (pFeatureIndex >= featuresCount) + { pFeatureIndex = 0; + } // In case of either default mode, MC years parallel computation is disabled (nb // of cores is set to 1) @@ -796,5 +856,3 @@ void Run::onInternalMotion(wxMouseEvent&) } } // namespace Antares::Window::Simulation - - From 271e62ca5e493b2b7f5dde86d8866fcd6d1176b7 Mon Sep 17 00:00:00 2001 From: Peter Mitri Date: Fri, 17 Jan 2025 15:02:51 +0100 Subject: [PATCH 4/6] fix Signed-off-by: Peter Mitri --- src/solver/optimisation/adequacy_patch_csr/solve_problem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver/optimisation/adequacy_patch_csr/solve_problem.cpp b/src/solver/optimisation/adequacy_patch_csr/solve_problem.cpp index 93d5fb3799..8da394395d 100644 --- a/src/solver/optimisation/adequacy_patch_csr/solve_problem.cpp +++ b/src/solver/optimisation/adequacy_patch_csr/solve_problem.cpp @@ -295,7 +295,7 @@ bool SolveWithSirius(const OptimizationOptions& options, << "Quadratic solver parameters are not supported by SIRIUS; they will be ignored."; } auto interiorPointProblem = buildInteriorPointProblem(ProblemeAResoudre); - PI_Quamin(buildInteriorPointProblem(ProblemeAResoudre).get()); // resolution + PI_Quamin(interiorPointProblem.get()); // resolution return interiorPointProblem->ExistenceDUneSolution == OUI_PI; } From c49ca2e4d0055829794aeafa952ae12a9eefd2aa Mon Sep 17 00:00:00 2001 From: Peter Mitri Date: Fri, 17 Jan 2025 15:06:08 +0100 Subject: [PATCH 5/6] fix Signed-off-by: Peter Mitri --- src/solver/utils/ortools_utils.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solver/utils/ortools_utils.cpp b/src/solver/utils/ortools_utils.cpp index 2985b1a926..3dce27d57b 100644 --- a/src/solver/utils/ortools_utils.cpp +++ b/src/solver/utils/ortools_utils.cpp @@ -340,9 +340,9 @@ const std::map OrtoolsUtils::mpSo {"glpk", {"glpk_lp", "glpk"}}, {"scip", {"scip", "scip"}}}; -const std::map OrtoolsUtils::mathoptSolverMap = { - {"scip", math_opt::SolverType::kGscip}, - {"xpress", math_opt::SolverType::kXpress}}; +const std::map OrtoolsUtils::mathoptSolverMap = {}; +// TODO: add skip when fixed by google {"scip", math_opt::SolverType::kGscip} +// TODO: add XPRESS when merged {"xpress", math_opt::SolverType::kXpress} std::list getAvailableLinearSolverNames() { From d5d90f08e43d596fc1980c0b74d34bd1a49744c5 Mon Sep 17 00:00:00 2001 From: Peter Mitri Date: Fri, 17 Jan 2025 17:41:14 +0100 Subject: [PATCH 6/6] cleanup Signed-off-by: Peter Mitri --- docs/user-guide/solver/02-command-line.md | 2 +- src/solver/application/application.cpp | 8 +++--- .../optimisation/post_process_commands.cpp | 3 +- .../solver/utils/ortools_quadratic_wrapper.h | 6 ++++ .../utils/ortools_quadratic_wrapper.cpp | 7 ----- src/solver/utils/ortools_utils.cpp | 15 ++-------- .../test_binding_constraints.cpp | 28 +------------------ 7 files changed, 15 insertions(+), 54 deletions(-) diff --git a/docs/user-guide/solver/02-command-line.md b/docs/user-guide/solver/02-command-line.md index 8334a3e9f5..0ba9651690 100644 --- a/docs/user-guide/solver/02-command-line.md +++ b/docs/user-guide/solver/02-command-line.md @@ -18,7 +18,7 @@ hide: | --parallel | Enable [parallel](optional-features/multi-threading.md) computation of MC years | | --force-parallel=VALUE | Override the max number of years computed [simultaneously](optional-features/multi-threading.md) | | --linear-solver=VALUE | The optimization solver to use for linear problems. Possible values are: `sirius` (default), `coin`, `xpress`, `scip` | -| --quadratic-solver=VALUE | The optimization solver to use for quadratic problems. Possible values are: `sirius` (default), `scip` | +| --quadratic-solver=VALUE | The optimization solver to use for quadratic problems. Possible values are: `sirius` (default) | ## Parameters diff --git a/src/solver/application/application.cpp b/src/solver/application/application.cpp index c01d1726ad..c0df0acb51 100644 --- a/src/solver/application/application.cpp +++ b/src/solver/application/application.cpp @@ -48,10 +48,10 @@ namespace { void printSolvers() { - std::cout << "Available linear solvers: " // NOSONAR - << availableOrToolsSolversString(SolverClass::LINEAR) << std::endl; - std::cout << "Available quadratic solvers: " // NOSONAR - << availableOrToolsSolversString(SolverClass::QUADRATIC) << std::endl; + std::cout << "Available linear solvers: " // NOSONAR + << availableOrToolsSolversString(SolverClass::LINEAR) << std::endl; // NOSONAR + std::cout << "Available quadratic solvers: " // NOSONAR + << availableOrToolsSolversString(SolverClass::QUADRATIC) << std::endl; // NOSONAR } } // namespace diff --git a/src/solver/optimisation/post_process_commands.cpp b/src/solver/optimisation/post_process_commands.cpp index 44723952e2..8ce0e8c546 100644 --- a/src/solver/optimisation/post_process_commands.cpp +++ b/src/solver/optimisation/post_process_commands.cpp @@ -276,8 +276,7 @@ void CurtailmentSharingPostProcessCmd::execute(const optRuntimeData& opt_runtime logs.info() << "[adq-patch] CSR triggered for Year:" << year + 1 << " Hour:" << week * nbHoursInWeek + hourInWeek + 1; hourlyCsrProblem.setHour(hourInWeek); - auto opt = opt_runtime_data.options; - hourlyCsrProblem.run(week, year, opt); + hourlyCsrProblem.run(week, year, opt_runtime_data.options); } } diff --git a/src/solver/utils/include/antares/solver/utils/ortools_quadratic_wrapper.h b/src/solver/utils/include/antares/solver/utils/ortools_quadratic_wrapper.h index 5147aa70b7..a823a4bfbb 100644 --- a/src/solver/utils/include/antares/solver/utils/ortools_quadratic_wrapper.h +++ b/src/solver/utils/include/antares/solver/utils/ortools_quadratic_wrapper.h @@ -24,6 +24,12 @@ #include #include +/** + * This wrapper is an adapter that solves a QP stored in a PROBLEME_ANTARES_A_RESOUDRE + * using OR-Tools MathOpt API & solvers. + * Currently, QP support in MathOpt is implemented for Gurobi and SIP (though SCIP in OR-Tools + * has compilation issues), and under development for XPRESS. + */ void SolveQuadraticProblemWithOrtools( const Antares::Solver::Optimization::OptimizationOptions& options, PROBLEME_ANTARES_A_RESOUDRE* ProblemeAResoudre); diff --git a/src/solver/utils/ortools_quadratic_wrapper.cpp b/src/solver/utils/ortools_quadratic_wrapper.cpp index 0bfaa8152c..de17180b18 100644 --- a/src/solver/utils/ortools_quadratic_wrapper.cpp +++ b/src/solver/utils/ortools_quadratic_wrapper.cpp @@ -27,13 +27,6 @@ #include #include -/** - * This wrapper is an adapter that solves a QP stored in a PROBLEME_ANTARES_A_RESOUDRE - * using OR-Tools MathOpt API & solvers. - * Currently, QP support in MathOpt is implemented for Gurobi and SIP (though SCIP in OR-Tools - * has compilation issues), and under development for XPRESS. - */ - using Antares::Solver::Optimization::OptimizationOptions; using namespace operations_research::math_opt; diff --git a/src/solver/utils/ortools_utils.cpp b/src/solver/utils/ortools_utils.cpp index 3dce27d57b..53d30ae9d7 100644 --- a/src/solver/utils/ortools_utils.cpp +++ b/src/solver/utils/ortools_utils.cpp @@ -21,8 +21,6 @@ #include "antares/solver/utils/ortools_utils.h" #include -#include -#include #include #include @@ -217,15 +215,6 @@ static int iLp = 0; bool solveAndManageStatus(MPSolver* solver, int& resultStatus, const MPSolverParameters& params) { - //++iLp; - // std::string filename = "/home/mitripet/debug_antares/lp_" + std::to_string(iLp) + ".lp"; - /*std::string lp; - solver->ExportModelAsLpFormat(false, &lp); - std::ofstream myfile; - myfile.open(filename); - myfile << lp; - myfile.close();*/ - // solver->Write(filename); auto status = solver->Solve(params); if (status == MPSolver::OPTIMAL || status == MPSolver::FEASIBLE) @@ -340,9 +329,9 @@ const std::map OrtoolsUtils::mpSo {"glpk", {"glpk_lp", "glpk"}}, {"scip", {"scip", "scip"}}}; +// TODO: add SCIP support when fixed by google: {"scip", math_opt::SolverType::kGscip} +// TODO: add XPRESS support when added in or-tools: {"xpress", math_opt::SolverType::kXpress} const std::map OrtoolsUtils::mathoptSolverMap = {}; -// TODO: add skip when fixed by google {"scip", math_opt::SolverType::kGscip} -// TODO: add XPRESS when merged {"xpress", math_opt::SolverType::kXpress} std::list getAvailableLinearSolverNames() { diff --git a/src/tests/end-to-end/binding_constraints/test_binding_constraints.cpp b/src/tests/end-to-end/binding_constraints/test_binding_constraints.cpp index fafc242892..70946bc22a 100644 --- a/src/tests/end-to-end/binding_constraints/test_binding_constraints.cpp +++ b/src/tests/end-to-end/binding_constraints/test_binding_constraints.cpp @@ -138,15 +138,6 @@ BOOST_FIXTURE_TEST_CASE(weekly_BC_restricts_link_direct_capacity_to_50, StudyWit OutputRetriever output(simulation->rawSimu()); unsigned int nbDaysInWeek = 7; BOOST_TEST(output.flow(link).week(0) == rhsValue * nbDaysInWeek, tt::tolerance(0.001)); - // TODO : revert these extra tests - BOOST_TEST(output.overallCost(area1).week(0) == 17500, tt::tolerance(0.001)); - BOOST_TEST(output.overallCost(area2).week(0) == 16450000, tt::tolerance(0.001)); - BOOST_TEST(output.load(area1).week(0) == 0, tt::tolerance(0.001)); - BOOST_TEST(output.load(area2).week(0) == 16800, tt::tolerance(0.001)); - BOOST_TEST(output.hydroStorage(area1).week(0) == 0, tt::tolerance(0.001)); - BOOST_TEST(output.hydroStorage(area2).week(0) == 0, tt::tolerance(0.001)); - BOOST_TEST(output.thermalGeneration(cluster.get()).week(0) == 350, tt::tolerance(0.001)); - BOOST_TEST(output.thermalNbUnitsON(cluster.get()).week(0) == 4, tt::tolerance(0.001)); } BOOST_FIXTURE_TEST_CASE(daily_BC_restricts_link_direct_capacity_to_60, StudyWithBConLink) @@ -250,15 +241,6 @@ BOOST_FIXTURE_TEST_CASE(On_year_2__RHS_TS_number_2_is_taken_into_account, StudyW OutputRetriever output(simulation->rawSimu()); BOOST_TEST(output.flow(link).hour(0) == bcGroupRHS2, tt::tolerance(0.001)); - // TODO : revert these extra tests - BOOST_TEST(output.overallCost(area1).week(0) == 588000, tt::tolerance(0.001)); - BOOST_TEST(output.overallCost(area2).week(0) == 5040000, tt::tolerance(0.001)); - BOOST_TEST(output.load(area1).week(0) == 0, tt::tolerance(0.001)); - BOOST_TEST(output.load(area2).week(0) == 16800, tt::tolerance(0.001)); - BOOST_TEST(output.hydroStorage(area1).week(0) == 0, tt::tolerance(0.001)); - BOOST_TEST(output.hydroStorage(area2).week(0) == 0, tt::tolerance(0.001)); - BOOST_TEST(output.thermalGeneration(cluster.get()).week(0) == 11760, tt::tolerance(0.001)); - BOOST_TEST(output.thermalNbUnitsON(cluster.get()).week(0) == 168, tt::tolerance(0.001)); } BOOST_FIXTURE_TEST_CASE(On_year_9__RHS_TS_number_4_is_taken_into_account, StudyWithBConLink) @@ -296,15 +278,7 @@ BOOST_FIXTURE_TEST_CASE(On_year_9__RHS_TS_number_4_is_taken_into_account, StudyW OutputRetriever output(simulation->rawSimu()); BOOST_TEST(output.flow(link).hour(0) == 40., tt::tolerance(0.001)); - // TODO : revert these extra tests - BOOST_TEST(output.overallCost(area1).week(0) == 336000, tt::tolerance(0.001)); - BOOST_TEST(output.overallCost(area2).week(0) == 10080000, tt::tolerance(0.001)); - BOOST_TEST(output.load(area1).week(0) == 0, tt::tolerance(0.001)); - BOOST_TEST(output.load(area2).week(0) == 16800, tt::tolerance(0.001)); - BOOST_TEST(output.hydroStorage(area1).week(0) == 0, tt::tolerance(0.001)); - BOOST_TEST(output.hydroStorage(area2).week(0) == 0, tt::tolerance(0.001)); - BOOST_TEST(output.thermalGeneration(cluster.get()).week(0) == 6720, tt::tolerance(0.001)); - BOOST_TEST(output.thermalNbUnitsON(cluster.get()).week(0) == 168, tt::tolerance(0.001)); + BOOST_TEST(output.flow(link).hour(0) == 40., tt::tolerance(0.001)); } BOOST_FIXTURE_TEST_CASE(On_year_9__RHS_TS_number_4_out_of_bound_use_random_fallback_to_Oth_column,