diff --git a/src/solver/infeasible-problem-analysis/CMakeLists.txt b/src/solver/infeasible-problem-analysis/CMakeLists.txt
index 424235facc..72b93ad153 100644
--- a/src/solver/infeasible-problem-analysis/CMakeLists.txt
+++ b/src/solver/infeasible-problem-analysis/CMakeLists.txt
@@ -10,8 +10,8 @@ set(SRC_INFEASIBLE_PROBLEM_ANALYSIS
include/antares/solver/infeasible-problem-analysis/unfeasible-pb-analyzer.h
include/antares/solver/infeasible-problem-analysis/report.h
report.cpp
- include/antares/solver/infeasible-problem-analysis/constraint.h
- constraint.cpp
+ include/antares/solver/infeasible-problem-analysis/watched-constraints.h
+ watched-constraints.cpp
)
add_library(infeasible_problem_analysis ${SRC_INFEASIBLE_PROBLEM_ANALYSIS})
diff --git a/src/solver/infeasible-problem-analysis/constraint-slack-analysis.cpp b/src/solver/infeasible-problem-analysis/constraint-slack-analysis.cpp
index 785d4670f8..099ac70db6 100644
--- a/src/solver/infeasible-problem-analysis/constraint-slack-analysis.cpp
+++ b/src/solver/infeasible-problem-analysis/constraint-slack-analysis.cpp
@@ -31,12 +31,23 @@
using namespace operations_research;
+namespace
+{
+bool compareSlackSolutions(const MPVariable* a, const MPVariable* b)
+{
+ return a->solution_value() > b->solution_value();
+}
+
+constexpr unsigned int nbMaxSlackVarsToKeep = 10;
+} // namespace
+
namespace Antares::Optimization
{
void ConstraintSlackAnalysis::run(MPSolver* problem)
{
- addSlackVariables(problem);
+ selectConstraintsToWatch(problem);
+ addSlackVariablesToConstraints(problem);
if (slackVariables_.empty())
{
logs.error() << title() << " : no constraints have been selected";
@@ -53,9 +64,21 @@ void ConstraintSlackAnalysis::run(MPSolver* problem)
}
hasDetectedInfeasibilityCause_ = true;
+
+ sortSlackVariablesByValue();
+ trimSlackVariables();
+}
+
+void ConstraintSlackAnalysis::selectConstraintsToWatch(MPSolver* problem)
+{
+ ConstraintsFactory factory;
+ std::regex rgx = factory.constraintsFilter();
+ std::ranges::copy_if(problem->constraints(),
+ std::back_inserter(constraintsToWatch_),
+ [&rgx](auto* c) { return std::regex_search(c->name(), rgx); });
}
-void ConstraintSlackAnalysis::addSlackVariables(MPSolver* problem)
+void ConstraintSlackAnalysis::addSlackVariablesToConstraints(MPSolver* problem)
{
/* Optimization:
We assess that less than 1 every 3 constraint will match
@@ -64,29 +87,21 @@ void ConstraintSlackAnalysis::addSlackVariables(MPSolver* problem)
*/
const unsigned int selectedConstraintsInverseRatio = 3;
slackVariables_.reserve(problem->NumConstraints() / selectedConstraintsInverseRatio);
- std::regex rgx(constraint_name_pattern);
const double infinity = MPSolver::infinity();
- for (MPConstraint* constraint: problem->constraints())
+ for (MPConstraint* c: constraintsToWatch_)
{
- if (std::regex_search(constraint->name(), rgx))
+ if (c->lb() > -infinity)
{
- if (constraint->lb() != -infinity)
- {
- const MPVariable* slack = problem->MakeNumVar(0,
- infinity,
- constraint->name() + "::low");
- constraint->SetCoefficient(slack, 1.);
- slackVariables_.push_back(slack);
- }
-
- if (constraint->ub() != infinity)
- {
- const MPVariable* slack = problem->MakeNumVar(0,
- infinity,
- constraint->name() + "::up");
- constraint->SetCoefficient(slack, -1.);
- slackVariables_.push_back(slack);
- }
+ const MPVariable* slack = problem->MakeNumVar(0, infinity, c->name() + "::low");
+ c->SetCoefficient(slack, 1.);
+ slackVariables_.push_back(slack);
+ }
+
+ if (c->ub() < infinity)
+ {
+ const MPVariable* slack = problem->MakeNumVar(0, infinity, c->name() + "::up");
+ c->SetCoefficient(slack, -1.);
+ slackVariables_.push_back(slack);
}
}
}
@@ -104,10 +119,22 @@ void ConstraintSlackAnalysis::buildObjective(MPSolver* problem) const
objective->SetMinimization();
}
+void ConstraintSlackAnalysis::sortSlackVariablesByValue()
+{
+ std::sort(std::begin(slackVariables_), std::end(slackVariables_), ::compareSlackSolutions);
+}
+
+void ConstraintSlackAnalysis::trimSlackVariables()
+{
+ unsigned int nbSlackVars = slackVariables_.size();
+ slackVariables_.resize(std::min(nbMaxSlackVarsToKeep, nbSlackVars));
+}
+
void ConstraintSlackAnalysis::printReport() const
{
InfeasibleProblemReport report(slackVariables_);
- report.prettyPrint();
+ report.logSuspiciousConstraints();
+ report.logInfeasibilityCauses();
}
} // namespace Antares::Optimization
diff --git a/src/solver/infeasible-problem-analysis/constraint.cpp b/src/solver/infeasible-problem-analysis/constraint.cpp
deleted file mode 100644
index 2c4fd6d085..0000000000
--- a/src/solver/infeasible-problem-analysis/constraint.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright 2007-2024, 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 "antares/solver/infeasible-problem-analysis/constraint.h"
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-namespace
-{
-const std::string kUnknown = "";
-}
-
-namespace Antares::Optimization
-{
-Constraint::Constraint(const std::string& name, const double slackValue):
- name_(name),
- slackValue_(slackValue)
-{
-}
-
-void Constraint::extractComponentsFromName()
-{
- boost::algorithm::split_regex(nameComponents_, name_, boost::regex("::"));
-}
-
-double Constraint::getSlackValue() const
-{
- return slackValue_;
-}
-
-class StringIsNotWellFormated: public std::runtime_error
-{
-public:
- StringIsNotWellFormated(const std::string& error_message):
- std::runtime_error(error_message)
- {
- }
-};
-
-std::string StringBetweenAngleBrackets(const std::string& constraintName)
-{
- std::vector split_name;
- boost::split(split_name, constraintName, boost::is_any_of("<>"));
-
- std::string err_msg = "Error: ";
- if (split_name.size() < 3)
- {
- err_msg += "constraint name '" + constraintName + "' misses '<' and/or '>' bracket";
- throw StringIsNotWellFormated(err_msg);
- }
- if (split_name[1].empty())
- {
- err_msg += "constraint name '" + constraintName + "' must be of format '**'";
- throw StringIsNotWellFormated(err_msg);
- }
- return split_name[1];
-}
-
-std::string Constraint::areaName() const
-{
- return StringBetweenAngleBrackets(nameComponents_.at(1));
-}
-
-std::string Constraint::timeStep() const
-{
- return StringBetweenAngleBrackets(nameComponents_.at(nameComponents_.size() - 2));
-}
-
-ConstraintType Constraint::type() const
-{
- assert(nameComponents_.size() > 1);
- if (nameComponents_.at(1) == "hourly")
- {
- return ConstraintType::binding_constraint_hourly;
- }
- if (nameComponents_.at(1) == "daily")
- {
- return ConstraintType::binding_constraint_daily;
- }
- if (nameComponents_.at(1) == "weekly")
- {
- return ConstraintType::binding_constraint_weekly;
- }
- if (nameComponents_.at(0) == "FictiveLoads")
- {
- return ConstraintType::fictitious_load;
- }
- if (nameComponents_.at(0) == "AreaHydroLevel")
- {
- return ConstraintType::hydro_reservoir_level;
- }
- if (nameComponents_.at(0) == "HydroPower")
- {
- return ConstraintType::hydro_production_weekly;
- }
- if (nameComponents_.at(0) == "Level")
- {
- return ConstraintType::short_term_storage_level;
- }
- return ConstraintType::none;
-}
-
-std::string Constraint::shortName() const
-{
- return nameComponents_.at(0);
-}
-
-std::string Constraint::STSname() const
-{
- return StringBetweenAngleBrackets(nameComponents_.at(2));
-}
-
-std::string Constraint::prettyPrint() const
-{
- switch (type())
- {
- case ConstraintType::binding_constraint_hourly:
- return "Hourly binding constraint '" + shortName() + "' at hour " + timeStep();
- case ConstraintType::binding_constraint_daily:
- return "Daily binding constraint '" + shortName() + "' at day " + timeStep();
- case ConstraintType::binding_constraint_weekly:
- return "Weekly binding constraint '" + shortName();
- case ConstraintType::fictitious_load:
- return "Last resort shedding status at area '" + areaName() + "' at hour " + timeStep();
- case ConstraintType::hydro_reservoir_level:
- return "Hydro reservoir constraint at area '" + areaName() + "' at hour " + timeStep();
- case ConstraintType::hydro_production_weekly:
- return "Hydro weekly production at area '" + areaName() + "'";
- case ConstraintType::short_term_storage_level:
- return "Short-term-storage reservoir constraint at area '" + areaName() + "' in STS '"
- + STSname() + "' at hour " + timeStep();
- default:
- return kUnknown;
- }
-}
-} // namespace Antares::Optimization
diff --git a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint-slack-analysis.h b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint-slack-analysis.h
index bb56228b2f..462d5f965f 100644
--- a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint-slack-analysis.h
+++ b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint-slack-analysis.h
@@ -23,9 +23,11 @@
#include
#include "unfeasibility-analysis.h"
+#include "watched-constraints.h"
namespace operations_research
{
+class MPConstraint;
class MPVariable;
class MPSolver;
} // namespace operations_research
@@ -40,7 +42,6 @@ namespace Antares::Optimization
class ConstraintSlackAnalysis: public UnfeasibilityAnalysis
{
public:
- ConstraintSlackAnalysis() = default;
~ConstraintSlackAnalysis() override = default;
void run(operations_research::MPSolver* problem) override;
@@ -52,13 +53,14 @@ class ConstraintSlackAnalysis: public UnfeasibilityAnalysis
}
private:
+ void selectConstraintsToWatch(operations_research::MPSolver* problem);
+ void addSlackVariablesToConstraints(operations_research::MPSolver* problem);
void buildObjective(operations_research::MPSolver* problem) const;
- void addSlackVariables(operations_research::MPSolver* problem);
+ void sortSlackVariablesByValue();
+ void trimSlackVariables();
+ std::vector constraintsToWatch_;
std::vector slackVariables_;
- const std::string constraint_name_pattern = "^AreaHydroLevel::|::hourly::|::daily::|::weekly::|"
- "^FictiveLoads::|^Level::|"
- "^HydroPower::";
};
} // namespace Antares::Optimization
diff --git a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint.h b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint.h
deleted file mode 100644
index 52fb799692..0000000000
--- a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2007-2024, 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
-
-namespace Antares::Optimization
-{
-enum class ConstraintType
-{
- binding_constraint_hourly,
- binding_constraint_daily,
- binding_constraint_weekly,
- fictitious_load,
- hydro_reservoir_level,
- hydro_production_weekly,
- short_term_storage_level,
- none
-};
-
-class Constraint
-{
-public:
- Constraint() = default;
- Constraint(const std::string& name, const double slackValue);
-
- double getSlackValue() const;
-
- void extractComponentsFromName();
- std::string prettyPrint() const;
- ConstraintType type() const;
-
-private:
- std::string name_;
- std::vector nameComponents_;
- double slackValue_;
-
- std::string areaName() const;
- std::string STSname() const;
- std::string timeStep() const;
- std::string shortName() const;
-};
-} // namespace Antares::Optimization
diff --git a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/report.h b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/report.h
index 898f4df359..ef242b1f36 100644
--- a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/report.h
+++ b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/report.h
@@ -21,10 +21,12 @@
#pragma once
#include