diff --git a/changelog.md b/changelog.md index 3c84d97e..edf49a50 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - [Doc:Unreleased] +### Added + +- [PR41](https://github.com/scipopt/SCIPpp/pull/41) Basic support of IIS extraction via `Model::generateIIS`. + This requires that the plugin `IISfinderGreedy` is loaded (which is done by default) or any other plugin that can + find IIS'. Example usage: + ```cpp + Model model = ...; + auto iis { model.generateIIS() }; + std::cout << "Contradicting are:\n"; + for (const auto& consId : iis.consIds) { + std::cout << " " << consId << "\n"; + } + ``` + ### Changed - [PR38](https://github.com/scipopt/SCIPpp/pull/38) Update to SCIP 10.0.0. diff --git a/include/scippp/iis.hpp b/include/scippp/iis.hpp new file mode 100644 index 00000000..e489c320 --- /dev/null +++ b/include/scippp/iis.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace scippp { + +/** + * A data structure to hold an %IIS (%Irreducible %Infeasible %Subsystem). + * @since 1.4.0 + */ +struct IIS { + //! Ids of the constraints in the %IIS. + std::vector consIds; +}; + +} diff --git a/include/scippp/model.hpp b/include/scippp/model.hpp index cac360ea..2d4e6af5 100644 --- a/include/scippp/model.hpp +++ b/include/scippp/model.hpp @@ -11,6 +11,7 @@ #include #include "scippp/constant_coefficient.hpp" +#include "scippp/iis.hpp" #include "scippp/initial_solution.hpp" #include "scippp/lin_expr.hpp" #include "scippp/lin_ineq.hpp" @@ -347,5 +348,13 @@ class Model { bool checkBounds = true, bool checkIntegrality = true, bool checkLpRows = true); + + /** + * Creates an %Irreducible %Infeasible %Subsystem (%IIS) of the current model. + * + * @since 1.4.0 + * @return The generated %IIS. + */ + [[nodiscard]] IIS generateIIS() const; }; } diff --git a/source/model.cpp b/source/model.cpp index 84117cbd..130f311c 100644 --- a/source/model.cpp +++ b/source/model.cpp @@ -175,4 +175,27 @@ bool Model::addSolution( return isStored; } +IIS Model::generateIIS() const +{ + m_scipCallWrapper(SCIPgenerateIIS(m_scip)); + auto* iis { SCIPgetIIS(m_scip) }; + assert(iis); // GCOVR_EXCL_LINE + auto* subscip { SCIPiisGetSubscip(iis) }; + assert(subscip); // GCOVR_EXCL_LINE + auto nConss { SCIPgetNOrigConss(subscip) }; + + IIS result; + if (nConss > 0) { + auto** conss { SCIPgetOrigConss(subscip) }; + assert(conss); // GCOVR_EXCL_LINE + result.consIds.reserve(nConss); + for (size_t i { 0 }; i < nConss; ++i) { + SCIP_CONS* cons = conss[i]; + assert(cons); // GCOVR_EXCL_LINE + result.consIds.emplace_back(SCIPconsGetName(cons)); + } + } + return result; +} + } diff --git a/test/test_iis.cpp b/test/test_iis.cpp new file mode 100644 index 00000000..286656ac --- /dev/null +++ b/test/test_iis.cpp @@ -0,0 +1,52 @@ +#include + +#include "scippp/model.hpp" +#include "scippp/parameters.hpp" + +#include + +using namespace scippp; +using namespace std; + +BOOST_AUTO_TEST_SUITE(IIS) + +BOOST_AUTO_TEST_CASE(CanBeCreated) +{ + Model model("Simple"); + const auto& [x1, x2] = model.addVars<2>("x_"); + model.addConstr(x1 + x2 >= 2, "lower"); + model.addConstr(x1 + x2 <= 1, "upper"); + model.addConstr(x1 + x2 >= 0, "useless"); + model.setObjsense(Sense::MINIMIZE); + + model.setParam(params::IIS::SILENT, true); // to avoid output during testing + model.solve(); + BOOST_REQUIRE(model.getStatus() == SCIP_STATUS_INFEASIBLE); + auto iis { model.generateIIS() }; + BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); + + const auto& icons { iis.consIds }; + BOOST_TEST(icons.size() == 2); + BOOST_TEST((std::find(icons.begin(), icons.end(), "lower") != iis.consIds.end())); + BOOST_TEST((std::find(icons.begin(), icons.end(), "upper") != iis.consIds.end())); + BOOST_TEST((std::find(icons.begin(), icons.end(), "useless") == iis.consIds.end())); +} + +BOOST_AUTO_TEST_CASE(WorksOnEmptyProblem) +{ + Model model("Simple"); + model.solve(); + auto iis { model.generateIIS() }; + BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); + BOOST_TEST(iis.consIds.size() == 0); +} + +BOOST_AUTO_TEST_CASE(WorksWithoutSolving) +{ + Model model("Simple"); + auto iis { model.generateIIS() }; + BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); + BOOST_TEST(iis.consIds.size() == 0); +} + +BOOST_AUTO_TEST_SUITE_END()