Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support more quadratic solvers #2574

Open
wants to merge 18 commits into
base: develop
Choose a base branch
from
6 changes: 4 additions & 2 deletions docs/user-guide/solver/02-command-line.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.

Expand Down
20 changes: 13 additions & 7 deletions docs/user-guide/solver/static-modeler/04-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions src/libs/antares/InfoCollection/StudyInfoCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Contributor

@guilpier-code guilpier-code Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About line :

std::string quadraticSolverName = study_.parameters.optOptions.linearSolver;

By any chance, would you mean (instead) :

std::string quadraticSolverName = study_.parameters.optOptions.quadraticSolver;

?

file_content.addItemToSection("study", "quadratic solver", quadraticSolverName);
}

// Collecting data optimization problem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/libs/antares/study/include/antares/study/parameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
13 changes: 9 additions & 4 deletions src/libs/antares/study/parameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment on lines +1286 to 1292
Copy link
Contributor

@guilpier-code guilpier-code Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of these 5 lines, it would be better to have :

optOptions = options.optOptions;

But because of the last line, it seems we can't do that.
But we may if we define an overloading operator in struct OptimizationOptions, with a special treatment for attribute solverLogs.
This operator could be "=" or "<<"

Expand Down Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions src/solver/application/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ namespace
{
void printSolvers()
Copy link
Contributor

@guilpier-code guilpier-code Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the scope of this PR ? (not sure)

printSolvers() :

  • Should be renamed into : logAllAvailableSolvers()
  • why do we print to std::cout and not in usual logs ? Does it even make sense ?

Moreover printSolvers is called from handleOptions which has a very non expressive and probably wrong name and, besides printing, does very strange things.

{
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

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/solver/misc/include/antares/solver/misc/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,5 @@ std::unique_ptr<Yuni::GetOpt::Parser> 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__ */
64 changes: 50 additions & 14 deletions src/solver/misc/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include <limits>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many headers inclusion unnecessary here :

  • algorithm,
  • cassert,
  • limits,
  • antares/study/study.h (already included in options.h)
  • yuni/yuni.h (already included in options.h)
  • string (already included in options.h)
  • antares/logs/logs.h
  • and maybe more

#include <string.h>

#include <boost/algorithm/string/join.hpp>

#include <yuni/yuni.h>

#include <antares/antares/constants.h>
Expand Down Expand Up @@ -75,22 +77,53 @@ std::unique_ptr<Yuni::GetOpt::Parser> 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,
Expand Down Expand Up @@ -246,7 +279,7 @@ void checkAndCorrectSettingsAndOptions(Settings& settings, Data::StudyLoadOption
}

options.checkForceSimulationMode();
checkOrtoolsSolver(options.optOptions);
checkSolvers(options);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should rename options.optOptions into options.solverOptions, and
call :

checkSolvers(options.solverOptions);

(indeed, options.optOptions is about solver options).
It would be more clear.


// no-output and force-zip-output
if (settings.noOutput && settings.forceZipOutput)
Expand All @@ -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<std::string> availableSolversList)
{
const std::string& solverName = optOptions.ortoolsSolver;
const std::list<std::string> 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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checkSolvers(...) could be renamed into checkForSolversExistence(...)

{
checkSolverExists(options.optOptions.linearSolver, getAvailableOrtoolsMpSolverName());
checkSolverExists(options.optOptions.quadraticSolver, getAvailableOrtoolsQuadraticSolverName());
}

void Settings::checkAndSetStudyFolder(const std::string& folder)
{
// The study folder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/solver/optimisation/opt_appel_solveur_lineaire.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ static SimplexResult OPT_TryToCallSimplex(const OptimizationOptions& options,
Probleme.NombreDeContraintesCoupes = 0;

auto ortoolsProblem = std::make_unique<LegacyOrtoolsLinearProblem>(Probleme.isMIP(),
options.ortoolsSolver);
options.linearSolver);
auto legacyOrtoolsFiller = std::make_unique<LegacyFiller>(&Probleme);
std::vector<LinearProblemFiller*> fillersCollection = {legacyOrtoolsFiller.get()};
LinearProblemData LP_Data;
Expand Down Expand Up @@ -353,7 +353,7 @@ bool OPT_AppelDuSimplexe(const OptimizationOptions& options,
Probleme.SetUseNamedProblems(true);

auto ortoolsProblem = std::make_unique<LegacyOrtoolsLinearProblem>(Probleme.isMIP(),
options.ortoolsSolver);
options.linearSolver);
auto legacyOrtoolsFiller = std::make_unique<LegacyFiller>(&Probleme);
std::vector<LinearProblemFiller*> fillersCollection = {legacyOrtoolsFiller.get()};
LinearProblemData LP_Data;
Expand Down
6 changes: 3 additions & 3 deletions src/solver/optimisation/opt_optimisation_hebdo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.quadraticSolver != "sirius")
{
const std::string notFound = "Solver " + options.quadraticSolver
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t understand this condition

+ " not supported for quadratic problems optimization.";
throw new std::invalid_argument(notFound);
}
if (!problemeHebdo->LeProblemeADejaEteInstancie)
{
OPT_ConstruireLaListeDesVariablesOptimiseesDuProblemeQuadratique(problemeHebdo);
Expand Down
30 changes: 25 additions & 5 deletions src/solver/utils/include/antares/solver/utils/ortools_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,43 @@

using namespace operations_research;

enum SolverClass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use enum class

{
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<std::string> 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<std::string> 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<std::string> getAvailableOrtoolsSolverName();
std::list<std::string> 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
Expand All @@ -68,7 +88,7 @@ class OrtoolsUtils
public:
struct SolverNames
{
std::string LPSolverName, MIPSolverName;
std::string LPSolverName, MIPSolverName, QuadraticSolverName;
};
static const std::map<std::string, struct SolverNames> solverMap;
};
Loading
Loading