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

3rd try at scenario building #2033

Draft
wants to merge 9 commits into
base: poc_linearProblem
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions src/solver/optim/api/LinearProblemBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,33 @@ void LinearProblemBuilder::addFiller(std::shared_ptr<LinearProblemFiller> filler
fillers_.push_back(filler);
}

void LinearProblemBuilder::build(const LinearProblemData& data) {
if (built) {
void LinearProblemBuilder::build(const LinearProblemData& data, const BuildContext& buildCtx)
{
if (built)
{
// TODO
throw;
}
for (auto filler : fillers_)
{
filler->addVariables(*linearProblem_, data);
filler->addVariables(*linearProblem_, data, buildCtx);
}
for (auto filler : fillers_)
{
filler->addConstraints(*linearProblem_, data);
filler->addConstraints(*linearProblem_, data, buildCtx);
}
for (auto filler : fillers_)
{
filler->addObjective(*linearProblem_, data);
filler->addObjective(*linearProblem_, data, buildCtx);
}
built = true;
}

void LinearProblemBuilder::update(const LinearProblemData& data) const {
void LinearProblemBuilder::update(const LinearProblemData& data) const
{
// TODO : throw if timestamps have changed ?
if (!built) {
if (!built)
{
// TODO
throw;
}
Expand All @@ -46,7 +50,8 @@ void LinearProblemBuilder::update(const LinearProblemData& data) const {
MipSolution LinearProblemBuilder::solve(const operations_research::MPSolverParameters& param)
{
// TODO : move to new interface LinearProblemSolver ??
if (!built) {
if (!built)
{
// TODO
throw;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ namespace Antares::optim::api
virtual operations_research::MPConstraint& addBalanceConstraint(std::string name, double bound, std::string nodeName, int timestep) = 0;
virtual operations_research::MPConstraint& getConstraint(std::string name) = 0;
virtual void setObjectiveCoefficient(const operations_research::MPVariable& variable, double coefficient) = 0;
virtual double getObjectiveCoefficient(const operations_research::MPVariable& variable) = 0;
virtual void setMinimization(bool isMinim) = 0;
virtual MipSolution solve(const operations_research::MPSolverParameters& param) = 0;
virtual double infinity() = 0;
virtual ~LinearProblem() = default;
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,23 @@
*/
#pragma once

#include <vector>
#include "LinearProblemFiller.h"
#include "vector"

namespace Antares::optim::api
{
class LinearProblemBuilder final
{
private:
LinearProblem* linearProblem_;
std::vector<std::shared_ptr<LinearProblemFiller>> fillers_{};
bool built = false;
public:
explicit LinearProblemBuilder(LinearProblem& linearProblem) : linearProblem_(&linearProblem) {};
void addFiller(std::shared_ptr<LinearProblemFiller> filler);
void build(const LinearProblemData& data);
void update(const LinearProblemData& data) const;
MipSolution solve(const operations_research::MPSolverParameters& param);
};
}
class LinearProblemBuilder final
{
private:
LinearProblem* linearProblem_;
std::vector<std::shared_ptr<LinearProblemFiller>> fillers_{};
bool built = false;

public:
explicit LinearProblemBuilder(LinearProblem& linearProblem) : linearProblem_(&linearProblem){};
void addFiller(std::shared_ptr<LinearProblemFiller> filler);
void build(const LinearProblemData& data, const BuildContext& ctx);
void update(const LinearProblemData& data) const;
MipSolution solve(const operations_research::MPSolverParameters& param);
};
} // namespace Antares::optim::api
117 changes: 84 additions & 33 deletions src/solver/optim/api/include/antares/optim/api/LinearProblemData.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,45 +27,96 @@
#pragma once

#include <utility>
#include <vector>
#include "antares/solver/simulation/sim_structure_probleme_economique.h"

#include "vector"

namespace Antares::optim::api
{
class LinearProblemData final
class LinearProblemData final
{
private:
// TODO : timestamps or timesteps?
int timeResolutionInMinutes_;
std::map<std::string, std::vector<double>> scalarData_;
std::map<std::string, std::vector<std::vector<double>>> timedData_;
// TODO : handle scenarios, and data vectorized on scenarios, on time, or on both
public:
explicit LinearProblemData(
int timeResolutionInMinutes,
const std::map<std::string, std::vector<double>>& scalarData,
const std::map<std::string, std::vector<std::vector<double>>>& timedData) :
Comment on lines +46 to +47
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const std::map<std::string, std::vector<double>>& scalarData,
const std::map<std::string, std::vector<std::vector<double>>>& timedData) :
const std::map<std::string, std::vector<double>>& scalarDataPerScenario,
const std::map<std::string, std::vector<std::vector<double>>>& timedDataPerScenario) :

timeResolutionInMinutes_(timeResolutionInMinutes),
scalarData_(scalarData),
timedData_(timedData){
// TODO: some coherence check on data
// for example, check that timed data are all of same size = size of timeStamps_
};
[[nodiscard]] int getTimeResolutionInMinutes() const
{
return timeResolutionInMinutes_;
}
[[nodiscard]] bool hasScalarData(const std::string& key) const
{
return scalarData_.contains(key);
}
[[nodiscard]] double getScalarData(const std::string& key, unsigned int scenario) const
{
private:
// TODO : timestamps or timesteps?
std::vector<int> timeStamps_;
int timeResolutionInMinutes_;
std::map<std::string, double> scalarData_;
std::map<std::string, std::vector<double>> timedData_;
// TODO : handle scenarios, and data vectorized on scenarios, on time, or on both
public:
explicit LinearProblemData(const std::vector<int> &timeStamps, int timeResolutionInMinutes,
const std::map<std::string, double> &scalarData,
const std::map<std::string, std::vector<double>> &timedData) :
timeStamps_(timeStamps), timeResolutionInMinutes_(timeResolutionInMinutes),
scalarData_(scalarData), timedData_(timedData)
switch (scalarData_.at(key).size())
{
// TODO: some coherence check on data
// for example, check that timed data are all of same size = size of timeStamps_
};
[[nodiscard]] std::vector<int> getTimeStamps() const { return timeStamps_; }
[[nodiscard]] int getTimeResolutionInMinutes() const { return timeResolutionInMinutes_; }
[[nodiscard]] bool hasScalarData(const std::string& key) const { return scalarData_.contains(key); }
[[nodiscard]] double getScalarData(const std::string& key) const { return scalarData_.at(key); }
[[nodiscard]] bool hasTimedData(const std::string& key) const { return timedData_.contains(key); }
[[nodiscard]] const std::vector<double>& getTimedData(const std::string& key) const { return timedData_.at(key); }
case 1:
return scalarData_.at(key)[0];
default:
return scalarData_.at(key)[scenario];
}
}
[[nodiscard]] bool hasTimedData(const std::string& key) const
{
return timedData_.contains(key);
}
[[nodiscard]] const std::vector<double>& getTimedData(const std::string& key,
unsigned int scenario) const
{
switch (timedData_.at(key).size())
{
case 1:
return timedData_.at(key)[0];
default:
return timedData_.at(key)[scenario];
}
}

// TODO: remove this when legacy support is dropped
// TODO: meanwhile, instead of having a nested struct, create a daughter class?
struct Legacy {
const std::vector<CORRESPONDANCES_DES_CONTRAINTES>* constraintMapping;
const std::vector<const char*>* areaNames;
};
Legacy legacy;
// TODO: remove this when legacy support is dropped
// TODO: meanwhile, instead of having a nested struct, create a daughter class?
struct Legacy
{
const std::vector<CORRESPONDANCES_DES_CONTRAINTES>* constraintMapping;
const std::vector<const char*>* areaNames;
};
Legacy legacy;
}; // namespace Antares::optim::api

// TODO[FOM] Move to dedicated header file
struct BuildContext final
{
using ScenarioID = unsigned int;
using TimeStampID = int;

[[nodiscard]] std::vector<TimeStampID> getTimeStamps() const
{
return timeStamps;
}

[[nodiscard]] std::vector<ScenarioID> getScenarios() const
{
return scenarios;
}

BuildContext(std::vector<ScenarioID> scenarios, std::vector<TimeStampID> timeStamps) :
scenarios(scenarios), timeStamps(timeStamps)
{
}
const std::vector<ScenarioID> scenarios;
const std::vector<TimeStampID> timeStamps;
};

}
} // namespace Antares::optim::api
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,31 @@

namespace Antares::optim::api
{
class LinearProblemFiller
{
public:
virtual void addVariables(LinearProblem& problem, const LinearProblemData& data) = 0;
virtual void addConstraints(LinearProblem& problem, const LinearProblemData& data) = 0;
virtual void addObjective(LinearProblem& problem, const LinearProblemData& data) = 0;
virtual void update(LinearProblem& problem, const LinearProblemData& data) = 0;
// TODO : see if update is really needed in target solution
// Currently used to update the MIP from week to week by only changing LB/UB & coefs
// (see sim_structure_contrainte_economique.h & ApportNaturelHoraire)
// This may be dropped in the target solution (thus we'll have to re-create the MIP) for 2 reasons:
// - we may have to add/remove variables & constraints
// - OR-Tools does not allow changing the names of variables & constraints, which is necessary if we want the
// variables & constraints to be indexed by the number of the week in the year
virtual ~LinearProblemFiller() = default;
};
}
class LinearProblemFiller
{
public:
virtual void addVariables(LinearProblem& problem,
const LinearProblemData& data,
const BuildContext&)
= 0;
virtual void addConstraints(LinearProblem& problem,
const LinearProblemData& data,
const BuildContext&)
= 0;
virtual void addObjective(LinearProblem& problem,
const LinearProblemData& data,
const BuildContext&)
= 0;
virtual void update(LinearProblem& problem, const LinearProblemData& data) = 0;
// TODO : see if update is really needed in target solution
// Currently used to update the MIP from week to week by only changing LB/UB & coefs
// (see sim_structure_contrainte_economique.h & ApportNaturelHoraire)
// This may be dropped in the target solution (thus we'll have to re-create the MIP) for 2
// reasons:
// - we may have to add/remove variables & constraints
// - OR-Tools does not allow changing the names of variables & constraints, which is necessary
// if we want the
// variables & constraints to be indexed by the number of the week in the year
virtual ~LinearProblemFiller() = default;
};
} // namespace Antares::optim::api
68 changes: 41 additions & 27 deletions src/solver/optim/api/include/antares/optim/api/MipSolution.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,40 +32,54 @@

namespace Antares::optim::api
{
class MipSolution final
class MipSolution final
{
// TODO: improve this by removing dependency to OR-Tools
private:
operations_research::MPSolver::ResultStatus responseStatus_;
std::map<std::string, double> solution_;
double objectiveValue_;

public:
MipSolution(operations_research::MPSolver::ResultStatus responseStatus,
const std::map<std::string, double>& solution,
double objectiveValue) :
responseStatus_(responseStatus), objectiveValue_(objectiveValue)
{
// TODO: improve this by removing dependency to OR-Tools
private:
operations_research::MPSolver::ResultStatus responseStatus_;
std::map<std::string, double> solution_;
public:
MipSolution(operations_research::MPSolver::ResultStatus responseStatus, const std::map<std::string, double>& solution) : responseStatus_(responseStatus)
// Only store non-zero values
for (const auto& varAndValue : solution)
{
// Only store non-zero values
for (const auto& varAndValue : solution)
if (abs(varAndValue.second) > 1e-6) // TODO: is this tolerance OK?
{
if (abs(varAndValue.second) > 1e-6) // TODO: is this tolerance OK?
{
solution_.insert(varAndValue);
}
solution_.insert(varAndValue);
}
}
}

operations_research::MPSolver::ResultStatus getStatus() { return responseStatus_; }
operations_research::MPSolver::ResultStatus getStatus()
{
return responseStatus_;
}

double getOptimalValue(const std::string& variableName)
{
return solution_.contains(variableName) ? solution_.at(variableName) : 0;
}
double getOptimalValue(const std::string& variableName)
{
return solution_.contains(variableName) ? solution_.at(variableName) : 0;
}

std::vector<double> getOptimalValues(const std::vector<std::string>& variableNames)
std::vector<double> getOptimalValues(const std::vector<std::string>& variableNames)
{
std::vector<double> solution;
solution.reserve(variableNames.size());
for (const auto& varName : variableNames)
{
std::vector<double> solution;
solution.reserve(variableNames.size());
for (const auto& varName : variableNames) {
solution.push_back(getOptimalValue(varName));
}
return solution;
solution.push_back(getOptimalValue(varName));
}
};
}
return solution;
}

double getObjectiveValue()
{
return objectiveValue_;
}
Comment on lines +80 to +83
Copy link
Member Author

Choose a reason for hiding this comment

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

objectiveValue_ is set at construction. I think it should be extracted after Solve. But then we have to manage a cache with synchronization issues, etc. So I suggest not caching the objective value for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

MipSolution is constructed after Solve

};
} // namespace Antares::optim::api
Loading
Loading