Skip to content

Commit

Permalink
Infeasibility more cleaning (#2231)
Browse files Browse the repository at this point in the history
We're on the road to make **infeasibility analyzer** more changeable,
more precisely when adding a constraint type to the list of constraints
to be detected in case of infeasibity.
This PR is a step towards this purpose.  
It contains some heterogeneous simplifications and renaming.
In order to ease the review, some comments were added in this PR.

---------

Co-authored-by: Florian OMNES <26088210+flomnes@users.noreply.github.com>
  • Loading branch information
guilpier-code and flomnes authored Jul 9, 2024
1 parent 27e140d commit 3fc885a
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 109 deletions.
116 changes: 32 additions & 84 deletions src/solver/infeasible-problem-analysis/constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <iomanip>
#include <sstream>

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

Expand All @@ -35,16 +36,15 @@ const std::string kUnknown = "<unknown>";

namespace Antares::Optimization
{
Constraint::Constraint(const std::string& input, const double slackValue):
name_(input),
Constraint::Constraint(const std::string& name, const double slackValue):
name_(name),
slackValue_(slackValue)
{
}

std::size_t Constraint::extractComponentsFromName()
void Constraint::extractComponentsFromName()
{
boost::algorithm::split_regex(nameComponents_, name_, boost::regex("::"));
return nameComponents_.size();
}

double Constraint::getSlackValue() const
Expand All @@ -61,67 +61,36 @@ class StringIsNotWellFormated: public std::runtime_error
}
};

std::string StringBetweenAngleBrackets(const std::string& str)
std::string StringBetweenAngleBrackets(const std::string& constraintName)
{
const auto& begin = str.begin();
const auto& end = str.end();
std::vector<std::string> split_name;
boost::split(split_name, constraintName, boost::is_any_of("<>"));

auto left = std::find(begin, end, '<');

if (left == end)
{
std::ostringstream stream;
stream << std::string("Error the string: ") << std::quoted(str)
<< " does not contains the left angle bracket " << std::quoted("<");
throw StringIsNotWellFormated(stream.str());
}

auto right = std::find(begin, end, '>');
if (right == end)
std::string err_msg = "Error: ";
if (split_name.size() < 3)
{
std::ostringstream stream;
stream << std::string("Error the string: ") << std::quoted(str)
<< " does not contains the right angle bracket " << std::quoted(">");
throw StringIsNotWellFormated(stream.str());
err_msg += "constraint name '" + constraintName + "' misses '<' and/or '>' bracket";
throw StringIsNotWellFormated(err_msg);
}

if (std::distance(left, right) <= 1)
if (split_name[1].empty())
{
std::ostringstream stream;
stream << std::string("Error the string: ") << std::quoted(str) << " must be of format "
<< std::quoted("*<str>*");
throw StringIsNotWellFormated(stream.str());
err_msg += "constraint name '" + constraintName + "' must be of format '*<str>*'";
throw StringIsNotWellFormated(err_msg);
}
return std::string(left + 1, right);
return split_name[1];
}

std::string Constraint::getAreaName() const
std::string Constraint::areaName() const
{
if ((getType() == ConstraintType::binding_constraint_hourly)
|| (getType() == ConstraintType::binding_constraint_daily)
|| (getType() == ConstraintType::binding_constraint_weekly))
{
return "<none>";
}
return StringBetweenAngleBrackets(nameComponents_.at(1));
}

std::string Constraint::getTimeStepInYear() const
std::string Constraint::timeStep() const
{
switch (getType())
{
case ConstraintType::binding_constraint_hourly:
case ConstraintType::binding_constraint_daily:
case ConstraintType::fictitious_load:
case ConstraintType::hydro_reservoir_level:
case ConstraintType::short_term_storage_level:
return StringBetweenAngleBrackets(nameComponents_.at(nameComponents_.size() - 2));
default:
return kUnknown;
}
return StringBetweenAngleBrackets(nameComponents_.at(nameComponents_.size() - 2));
}

ConstraintType Constraint::getType() const
ConstraintType Constraint::type() const
{
assert(nameComponents_.size() > 1);
if (nameComponents_.at(1) == "hourly")
Expand Down Expand Up @@ -155,56 +124,35 @@ ConstraintType Constraint::getType() const
return ConstraintType::none;
}

std::string Constraint::getBindingConstraintName() const
std::string Constraint::shortName() const
{
switch (getType())
{
case ConstraintType::binding_constraint_hourly:
case ConstraintType::binding_constraint_daily:
case ConstraintType::binding_constraint_weekly:
return nameComponents_.at(0);
default:
return kUnknown;
}
return nameComponents_.at(0);
}

std::string Constraint::getSTSName() const
std::string Constraint::STSname() const
{
if (getType() == ConstraintType::short_term_storage_level)
{
return StringBetweenAngleBrackets(nameComponents_.at(2));
}
else
{
return kUnknown;
}
return StringBetweenAngleBrackets(nameComponents_.at(2));
}

std::string Constraint::prettyPrint() const
{
switch (getType())
switch (type())
{
case ConstraintType::binding_constraint_hourly:
return "Hourly binding constraint '" + getBindingConstraintName() + "' at hour "
+ getTimeStepInYear();
return "Hourly binding constraint '" + shortName() + "' at hour " + timeStep();
case ConstraintType::binding_constraint_daily:
return "Daily binding constraint '" + getBindingConstraintName() + "' at day "
+ getTimeStepInYear();
return "Daily binding constraint '" + shortName() + "' at day " + timeStep();
case ConstraintType::binding_constraint_weekly:
return "Weekly binding constraint '" + getBindingConstraintName();

return "Weekly binding constraint '" + shortName();
case ConstraintType::fictitious_load:
return "Last resort shedding status at area '" + getAreaName() + "' at hour "
+ getTimeStepInYear();
return "Last resort shedding status at area '" + areaName() + "' at hour " + timeStep();
case ConstraintType::hydro_reservoir_level:
return "Hydro reservoir constraint at area '" + getAreaName() + "' at hour "
+ getTimeStepInYear();
return "Hydro reservoir constraint at area '" + areaName() + "' at hour " + timeStep();
case ConstraintType::hydro_production_weekly:
return "Hydro weekly production at area '" + getAreaName() + "'";
return "Hydro weekly production at area '" + areaName() + "'";
case ConstraintType::short_term_storage_level:
return "Short-term-storage reservoir constraint at area '" + getAreaName() + "' in STS '"
+ getSTSName() + "' at hour " + getTimeStepInYear();

return "Short-term-storage reservoir constraint at area '" + areaName() + "' in STS '"
+ STSname() + "' at hour " + timeStep();
default:
return kUnknown;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,27 +40,23 @@ enum class ConstraintType
class Constraint
{
public:
// Construct object
Constraint() = default;
Constraint(const std::string& input, const double slackValue);
Constraint(const std::string& name, const double slackValue);

// Raw members
double getSlackValue() const;

// Extract items, check consistency
std::size_t extractComponentsFromName();
void extractComponentsFromName();
std::string prettyPrint() const;
ConstraintType getType() const;
ConstraintType type() const;

private:
std::string name_;
std::vector<std::string> nameComponents_;
double slackValue_;

// Get specific items
std::string getAreaName() const;
std::string getSTSName() const;
std::string getTimeStepInYear() const;
std::string getBindingConstraintName() const;
std::string areaName() const;
std::string STSname() const;
std::string timeStep() const;
std::string shortName() const;
};
} // namespace Antares::Optimization
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ class InfeasibleProblemReport
private:
void turnSlackVarsIntoConstraints(
const std::vector<const operations_research::MPVariable*>& slackVariables);
void sortConstraints();
void sortConstraintsBySlackValue();
void trimConstraints();
void sortConstraintsByType();
void logSuspiciousConstraints();
void logInfeasibilityCauses();

std::vector<Constraint> constraints_;
std::map<ConstraintType, unsigned int> nbConstraintsByType_;
Expand Down
20 changes: 10 additions & 10 deletions src/solver/infeasible-problem-analysis/report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ InfeasibleProblemReport::InfeasibleProblemReport(
const std::vector<const MPVariable*>& slackVariables)
{
turnSlackVarsIntoConstraints(slackVariables);
sortConstraints();
sortConstraintsBySlackValue();
trimConstraints();
sortConstraintsByType();
}

void InfeasibleProblemReport::turnSlackVarsIntoConstraints(
Expand All @@ -56,7 +57,7 @@ void InfeasibleProblemReport::turnSlackVarsIntoConstraints(
}
}

void InfeasibleProblemReport::sortConstraints()
void InfeasibleProblemReport::sortConstraintsBySlackValue()
{
std::sort(std::begin(constraints_), std::end(constraints_), ::compareSlackSolutions);
}
Expand All @@ -71,11 +72,8 @@ void InfeasibleProblemReport::sortConstraintsByType()
{
for (auto& c: constraints_)
{
if (c.extractComponentsFromName() == 0)
{
return;
}
nbConstraintsByType_[c.getType()]++;
c.extractComponentsFromName();
nbConstraintsByType_[c.type()]++;
}
}

Expand All @@ -86,6 +84,10 @@ void InfeasibleProblemReport::logSuspiciousConstraints()
{
Antares::logs.error() << c.prettyPrint();
}
}

void InfeasibleProblemReport::logInfeasibilityCauses()
{
Antares::logs.error() << "Possible causes of infeasibility:";
if (nbConstraintsByType_[ConstraintType::hydro_reservoir_level] > 0)
{
Expand Down Expand Up @@ -114,14 +116,12 @@ void InfeasibleProblemReport::logSuspiciousConstraints()
{
Antares::logs.error() << "* Binding constraints,";
}

Antares::logs.error() << "* Negative hurdle costs on lines with infinite capacity (rare).";
}

void InfeasibleProblemReport::prettyPrint()
{
sortConstraintsByType();
logSuspiciousConstraints();
logInfeasibilityCauses();
}

} // namespace Antares::Optimization
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,9 @@ std::unique_ptr<MPSolver> createUnfeasibleProblem(const std::string& constraintN
}

static const std::string validConstraintNames[] = {
"BC::hourly::hour<36>",
"BC::daily::day<67>",
"BC::weekly::week<12>",
"BC-name-1::hourly::hour<36>",
"BC-name-2::daily::day<67>",
"BC-name-3::weekly::week<12>",
"FictiveLoads::hour<25>",
"AreaHydroLevel::hour<8>",
};
Expand Down

0 comments on commit 3fc885a

Please sign in to comment.