Skip to content

Commit

Permalink
Infeasibility analyzer : improve unit tests (#2249)
Browse files Browse the repository at this point in the history
Mainly add new unit tests for slack analysis.

Also add condition (threshold) on slack variable values.

---------

Co-authored-by: Florian OMNES <[email protected]>
Co-authored-by: Florian Omnès <[email protected]>
  • Loading branch information
3 people authored Jul 22, 2024
1 parent 67f31e4 commit bc23762
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ using namespace operations_research;

namespace
{
bool compareSlackSolutions(const MPVariable* a, const MPVariable* b)
bool greaterSlackSolutions(const MPVariable* a, const MPVariable* b)
{
return a->solution_value() > b->solution_value();
}
Expand All @@ -50,7 +50,7 @@ void ConstraintSlackAnalysis::run(MPSolver* problem)
addSlackVariablesToConstraints(problem);
if (slackVariables_.empty())
{
logs.error() << title() << " : no constraints have been selected";
logs.warning() << title() << " : no constraints have been selected";
return;
}

Expand All @@ -63,10 +63,12 @@ void ConstraintSlackAnalysis::run(MPSolver* problem)
return;
}

hasDetectedInfeasibilityCause_ = true;

sortSlackVariablesByValue();
trimSlackVariables();
if (hasDetectedInfeasibilityCause_ = anySlackVariableNonZero(); !hasDetectedInfeasibilityCause_)
{
logs.warning() << title() << " : no violation detected for selected constraints.";
}
}

void ConstraintSlackAnalysis::selectConstraintsToWatch(MPSolver* problem)
Expand Down Expand Up @@ -121,7 +123,7 @@ void ConstraintSlackAnalysis::buildObjective(MPSolver* problem) const

void ConstraintSlackAnalysis::sortSlackVariablesByValue()
{
std::sort(std::begin(slackVariables_), std::end(slackVariables_), ::compareSlackSolutions);
std::sort(std::begin(slackVariables_), std::end(slackVariables_), ::greaterSlackSolutions);
}

void ConstraintSlackAnalysis::trimSlackVariables()
Expand All @@ -130,11 +132,24 @@ void ConstraintSlackAnalysis::trimSlackVariables()
slackVariables_.resize(std::min(nbMaxSlackVarsToKeep, nbSlackVars));
}

bool ConstraintSlackAnalysis::anySlackVariableNonZero()
{
return std::ranges::any_of(slackVariables_,
[&](auto& v) { return v->solution_value() > thresholdNonZero; });
}

const std::vector<const operations_research::MPVariable*>&
ConstraintSlackAnalysis::largestSlackVariables()
{
return slackVariables_;
}

void ConstraintSlackAnalysis::printReport() const
{
InfeasibleProblemReport report(slackVariables_);
report.logSuspiciousConstraints();
report.logInfeasibilityCauses();
report.storeSuspiciousConstraints();
report.storeInfeasibilityCauses();
std::ranges::for_each(report.getLogs(), [](auto& line) { logs.notice() << line; });
}

} // namespace Antares::Optimization
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,19 @@ class ConstraintSlackAnalysis: public UnfeasibilityAnalysis
return "Slack variables analysis";
}

const std::vector<const operations_research::MPVariable*>& largestSlackVariables();

private:
void selectConstraintsToWatch(operations_research::MPSolver* problem);
void addSlackVariablesToConstraints(operations_research::MPSolver* problem);
void buildObjective(operations_research::MPSolver* problem) const;
void sortSlackVariablesByValue();
void trimSlackVariables();
bool anySlackVariableNonZero();

std::vector<operations_research::MPConstraint*> constraintsToWatch_;
std::vector<const operations_research::MPVariable*> slackVariables_;
const double thresholdNonZero = 1e-06;
};

} // namespace Antares::Optimization
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ class InfeasibleProblemReport
public:
InfeasibleProblemReport() = delete;
explicit InfeasibleProblemReport(const std::vector<const operations_research::MPVariable*>&);
void logSuspiciousConstraints();
void logInfeasibilityCauses();
void storeSuspiciousConstraints();
void storeInfeasibilityCauses();
std::vector<std::string> getLogs();

private:
void buildConstraintsFromSlackVars(const std::vector<const operations_research::MPVariable*>&);
void filterConstraintsToOneByType();

std::vector<std::shared_ptr<WatchedConstraint>> constraints_;
std::vector<std::string> report_;
};
} // namespace Antares::Optimization
21 changes: 12 additions & 9 deletions src/solver/infeasible-problem-analysis/report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
#include <regex>
#include <typeindex>

#include <antares/logs/logs.h>

namespace Antares::Optimization
{
InfeasibleProblemReport::InfeasibleProblemReport(
Expand Down Expand Up @@ -69,31 +67,36 @@ void InfeasibleProblemReport::filterConstraintsToOneByType()
{
// 1. Grouping constraints by C++ type (inside a group, order of instances remains unchanged)
std::ranges::stable_sort(constraints_, lessTypeName);
// 2. Keeping the first instances of each group, and rejecting others (= duplicates) to the end
// of vector
// 2. Keeping the first instances of each group, and rejecting others (= duplicates) to the end.
auto duplicates = std::ranges::unique(constraints_, sameType);
// 3. Removing trailing duplicates
constraints_.erase(duplicates.begin(), duplicates.end());
// 4. Sorting remaining constraints by slack value (in descending order)
std::ranges::sort(constraints_, greaterValue);
}

void InfeasibleProblemReport::logSuspiciousConstraints()
void InfeasibleProblemReport::storeSuspiciousConstraints()
{
report_.push_back("Violated constraints:");
for (const auto& c: constraints_)
{
Antares::logs.error() << c->infeasibility();
report_.push_back(c->infeasibility());
}
}

void InfeasibleProblemReport::logInfeasibilityCauses()
void InfeasibleProblemReport::storeInfeasibilityCauses()
{
filterConstraintsToOneByType();
Antares::logs.error() << "Possible causes of infeasibility:";
report_.push_back("Possible causes of infeasibility:");
for (const auto& c: constraints_)
{
Antares::logs.error() << c->infeasibilityCause();
report_.push_back(c->infeasibilityCause());
}
}

std::vector<std::string> InfeasibleProblemReport::getLogs()
{
return report_;
}

} // namespace Antares::Optimization
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ std::string StringBetweenAngleBrackets(const std::string& constraintName)

std::string timeStep(std::vector<std::string> splitName)
{
return StringBetweenAngleBrackets(splitName.at(splitName.size() - 2));
return StringBetweenAngleBrackets(splitName.rbegin()[1]);
}

std::string shortName(std::vector<std::string> splitName)
Expand Down
Loading

0 comments on commit bc23762

Please sign in to comment.