Skip to content

Commit cdb67ff

Browse files
committed
Corrected highspy method for getIis; added IIS test to test_highspy.py; added example of getIis to call_highs_from_python.py
1 parent daac261 commit cdb67ff

File tree

6 files changed

+154
-30
lines changed

6 files changed

+154
-30
lines changed

examples/call_highs_from_python.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,66 @@ def user_callback(
384384

385385
print(f"user_callback_data = {user_callback_data}: Success!")
386386
h.clearSolver()
387-
388387

388+
# ~~~
389+
# Clear so that incumbent model is empty
390+
h.clear()
391+
392+
# Build an infeasible LP problem
393+
# Problem: minimize x + y
394+
# Subject to: x + y <= 0.5 and x + y >= 2.0 (contradictory constraints)
395+
# Bounds: 0 <= x <= 1, 0 <= y <= 1
396+
397+
lp = highspy.HighsLp()
398+
lp.num_col_ = 2
399+
lp.num_row_ = 2
400+
401+
# Objective: minimize x + y
402+
lp.col_cost_ = [1.0, 1.0]
403+
404+
# Variable bounds: 0 <= x <= 1, 0 <= y <= 1
405+
lp.col_lower_ = [0.0, 0.0]
406+
lp.col_upper_ = [1.0, 1.0]
407+
408+
# Constraints:
409+
# Row 0: x + y <= 0.5
410+
# Row 1: -x - y <= -2.0 (equivalent to x + y >= 2.0)
411+
lp.row_lower_ = [-np.inf, -np.inf]
412+
lp.row_upper_ = [0.5, -2.0]
413+
414+
# Constraint matrix (CSC format)
415+
lp.a_matrix_.start_ = [0, 2, 4]
416+
lp.a_matrix_.index_ = [0, 1, 0, 1] # Row indices
417+
lp.a_matrix_.value_ = [1.0, 1.0, -1.0, -1.0] # Coefficients
418+
419+
# Pass model to HiGHS
420+
h.passModel(lp)
421+
422+
# Solve the problem
423+
h.run()
424+
425+
model_status = h.getModelStatus()
426+
427+
# Change the IIS strategy from the default (use the "light" strategy
428+
# of testing for incompatible bounds and bounds on row activities that
429+
# are incomparible with row bounds) to an attempt to find an IIS -
430+
# although the "light" strategy is sufficient for this LP
431+
432+
h.setOptionValue("iis_strategy", highspy.IisStrategy.kIisStrategyIrreducible)
433+
434+
[status, iis] = h.getIis()
435+
436+
iis_num_col = len(iis.col_index_)
437+
for iX in range(iis_num_col) :
438+
print("IIS col ", iX, " is ", iis.col_index_[iX], " with bound status ", iis.col_bound_[iX])
389439

440+
iis_num_row = len(iis.row_index_)
441+
for iX in range(iis_num_row) :
442+
print("IIS row ", iX, " is ", iis.row_index_[iX], " with bound status ", iis.row_bound_[iX])
390443

444+
# Status -1 => Not in conflict; 0 => Maybe in conflict; 1 => in conflict
445+
for iX in range(lp.num_col_) :
446+
print("Col ", iX, " has IIS status ", iis.col_status_[iX])
391447

448+
for iX in range(lp.num_row_) :
449+
print("Row ", iX, " has IIS status ", iis.row_status_[iX])

highs/highs_bindings.cpp

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,12 @@ std::tuple<HighsStatus, HighsRanging> highs_getRanging(Highs* h) {
192192
return std::make_tuple(status, ranging);
193193
}
194194

195+
std::tuple<HighsStatus, HighsIis> highs_getIis(Highs* h) {
196+
HighsIis iis;
197+
HighsStatus status = h->getIis(iis);
198+
return std::make_tuple(status, iis);
199+
}
200+
195201
std::tuple<HighsStatus, dense_array_t<HighsInt>> highs_getBasicVariables(
196202
Highs* h) {
197203
HighsInt num_row = h->getNumRow();
@@ -1062,17 +1068,27 @@ PYBIND11_MODULE(_core, m, py::mod_gil_not_used()) {
10621068
.value("kIisStrategyLight", IisStrategy::kIisStrategyLight)
10631069
.value("kIisStrategyFromRay", IisStrategy::kIisStrategyFromRay)
10641070
.value("kIisStrategyFromLp", IisStrategy::kIisStrategyFromLp)
1071+
.value("kIisStrategyIrreducible", IisStrategy::kIisStrategyIrreducible)
10651072
.value("kIisStrategyColPriority", IisStrategy::kIisStrategyColPriority)
1073+
.value("kIisStrategyRelaxation", IisStrategy::kIisStrategyRelaxation)
10661074
.value("kIisStrategyMax", IisStrategy::kIisStrategyMax)
10671075
.export_values();
1076+
10681077
py::enum_<IisBoundStatus>(m, "IisBoundStatus", py::module_local())
1069-
.value("kIisBoundStatusDropped", IisBoundStatus::kIisBoundStatusDropped)
1070-
.value("kIisBoundStatusNull", IisBoundStatus::kIisBoundStatusNull)
1071-
.value("kIisBoundStatusFree", IisBoundStatus::kIisBoundStatusFree)
1072-
.value("kIisBoundStatusLower", IisBoundStatus::kIisBoundStatusLower)
1073-
.value("kIisBoundStatusUpper", IisBoundStatus::kIisBoundStatusUpper)
1074-
.value("kIisBoundStatusBoxed", IisBoundStatus::kIisBoundStatusBoxed)
1075-
.export_values();
1078+
.value("kIisBoundStatusDropped", IisBoundStatus::kIisBoundStatusDropped)
1079+
.value("kIisBoundStatusNull", IisBoundStatus::kIisBoundStatusNull)
1080+
.value("kIisBoundStatusFree", IisBoundStatus::kIisBoundStatusFree)
1081+
.value("kIisBoundStatusLower", IisBoundStatus::kIisBoundStatusLower)
1082+
.value("kIisBoundStatusUpper", IisBoundStatus::kIisBoundStatusUpper)
1083+
.value("kIisBoundStatusBoxed", IisBoundStatus::kIisBoundStatusBoxed)
1084+
.export_values();
1085+
1086+
py::enum_<IisStatus>(m, "IisStatus", py::module_local())
1087+
.value("kIisStatusNotInConflict", IisStatus::kIisStatusNotInConflict)
1088+
.value("kIisStatusMaybeInConflict", IisStatus::kIisStatusMaybeInConflict)
1089+
.value("kIisStatusInConflict", IisStatus::kIisStatusInConflict)
1090+
.export_values();
1091+
10761092
py::enum_<HighsDebugLevel>(m, "HighsDebugLevel", py::module_local())
10771093
.value("kHighsDebugLevelNone", HighsDebugLevel::kHighsDebugLevelNone)
10781094
.value("kHighsDebugLevelCheap", HighsDebugLevel::kHighsDebugLevelCheap)
@@ -1369,7 +1385,7 @@ PYBIND11_MODULE(_core, m, py::mod_gil_not_used()) {
13691385
py::arg("local_lower_penalty") = py::none(),
13701386
py::arg("local_upper_penalty") = py::none(),
13711387
py::arg("local_rhs_penalty") = py::none())
1372-
.def("getIis", &Highs::getIis)
1388+
.def("getIis", &highs_getIis)
13731389
.def("presolve", &Highs::presolve,
13741390
py::call_guard<py::gil_scoped_release>())
13751391
.def("writeSolution", &highs_writeSolution)
@@ -1515,15 +1531,18 @@ PYBIND11_MODULE(_core, m, py::mod_gil_not_used()) {
15151531
py::class_<HighsIis>(m, "HighsIis", py::module_local())
15161532
.def(py::init<>())
15171533
.def("clear", &HighsIis::clear)
1518-
.def_readwrite("valid", &HighsIis::valid_)
1519-
.def_readwrite("status", &HighsIis::status_)
1520-
.def_readwrite("strategy", &HighsIis::strategy_)
1521-
.def_readwrite("col_index", &HighsIis::col_index_)
1522-
.def_readwrite("row_index", &HighsIis::row_index_)
1523-
.def_readwrite("col_bound", &HighsIis::col_bound_)
1524-
.def_readwrite("row_bound", &HighsIis::row_bound_)
1525-
.def_readwrite("info", &HighsIis::info_)
1526-
.def_readwrite("model", &HighsIis::model_);
1534+
.def_readwrite("valid_", &HighsIis::valid_)
1535+
.def_readwrite("status_", &HighsIis::status_)
1536+
.def_readwrite("strategy_", &HighsIis::strategy_)
1537+
.def_readwrite("col_index_", &HighsIis::col_index_)
1538+
.def_readwrite("row_index_", &HighsIis::row_index_)
1539+
.def_readwrite("col_bound_", &HighsIis::col_bound_)
1540+
.def_readwrite("row_bound_", &HighsIis::row_bound_)
1541+
.def_readwrite("col_status_", &HighsIis::col_status_)
1542+
.def_readwrite("row_status_", &HighsIis::row_status_)
1543+
.def_readwrite("info_", &HighsIis::info_)
1544+
.def_readwrite("model_", &HighsIis::model_);
1545+
15271546
// structs
15281547
py::class_<HighsSolution>(m, "HighsSolution", py::module_local())
15291548
.def(py::init<>())
@@ -1727,16 +1746,6 @@ PYBIND11_MODULE(_core, m, py::mod_gil_not_used()) {
17271746
.value("kSteepestEdge", EdgeWeightMode::kSteepestEdge)
17281747
.value("kCount", EdgeWeightMode::kCount);
17291748

1730-
/*
1731-
py::module_ iis = m.def_submodule("iis", "IIS interface submodule");
1732-
py::enum_<HighsIisStatus>(iis, "HighsIisStatus",
1733-
py::module_local())
1734-
.value("kIisStatusInConflict", HighsIisStatus::kIisStatusInConflict)
1735-
.value("kIisStatusNotInConflict", HighsIisStatus::kIisStatusNotInConflict)
1736-
.value("kIisStatusMaybeInConflict", HighsIisStatus::kIisStatusMaybeInConflict)
1737-
.export_values();
1738-
*/
1739-
17401749
py::module_ callbacks = m.def_submodule("cb", "Callback interface submodule");
17411750
// Types for interface
17421751
py::enum_<HighsCallbackType>(callbacks, "HighsCallbackType",

highs/highspy/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
HighsLogType,
1515
IisStrategy,
1616
IisBoundStatus,
17+
IisStatus,
1718
HighsSparseMatrix,
1819
HighsLp,
1920
HighsHessian,

highs/lp_data/HConst.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ enum IisStrategy : int {
293293
kIisStrategyRelaxation
294294
};
295295

296-
enum IisStatus {
296+
enum IisStatus : int {
297297
kIisStatusMin = -1,
298298
kIisStatusNotInConflict = kIisStatusMin, // -1
299299
kIisStatusMaybeInConflict, // 0

highs/lp_data/HighsIis.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
const bool kIisDevReport = false;
1717

18-
enum IisBoundStatus {
18+
enum IisBoundStatus : int {
1919
kIisBoundStatusDropped = -1,
2020
kIisBoundStatusNull, // 0
2121
kIisBoundStatusFree, // 1

tests/test_highspy.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2252,3 +2252,59 @@ def test_get_objectives(self):
22522252
self.assertEqual(obj_expr2.vals, [1.0, 2.0, 3.0])
22532253
self.assertEqual(obj_expr2.constant, 5.0)
22542254
self.assertEqual(sense2, highspy.ObjSense.kMaximize)
2255+
2256+
def test_get_iis(self):
2257+
h = highspy.Highs()
2258+
# h.silent()
2259+
# Build an infeasible LP problem
2260+
# Problem: minimize x + y
2261+
# Subject to: x + y <= 0.5 and x + y >= 2.0 (contradictory constraints)
2262+
# Bounds: 0 <= x <= 1, 0 <= y <= 1
2263+
2264+
lp = highspy.HighsLp()
2265+
lp.num_col_ = 2
2266+
lp.num_row_ = 2
2267+
2268+
# Objective: minimize x + y
2269+
lp.col_cost_ = [1.0, 1.0]
2270+
2271+
# Variable bounds: 0 <= x <= 1, 0 <= y <= 1
2272+
lp.col_lower_ = [0.0, 0.0]
2273+
lp.col_upper_ = [1.0, 1.0]
2274+
2275+
# Constraints:
2276+
# Row 0: x + y <= 0.5
2277+
# Row 1: -x - y <= -2.0 (equivalent to x + y >= 2.0)
2278+
lp.row_lower_ = [-np.inf, -np.inf]
2279+
lp.row_upper_ = [0.5, -2.0]
2280+
2281+
# Constraint matrix (CSC format)
2282+
lp.a_matrix_.start_ = [0, 2, 4]
2283+
lp.a_matrix_.index_ = [0, 1, 0, 1] # Row indices
2284+
lp.a_matrix_.value_ = [1.0, 1.0, -1.0, -1.0] # Coefficients
2285+
2286+
# Pass model to HiGHS
2287+
h.passModel(lp)
2288+
2289+
# Solve the problem
2290+
h.run()
2291+
2292+
model_status = h.getModelStatus()
2293+
self.assertEqual(model_status, highspy.HighsModelStatus.kInfeasible)
2294+
2295+
h.setOptionValue("iis_strategy", highspy.IisStrategy.kIisStrategyIrreducible)
2296+
[status, iis] = h.getIis()
2297+
2298+
self.assertEqual(iis.valid_, True)
2299+
self.assertEqual(iis.col_index_[0], 0)
2300+
self.assertEqual(iis.col_index_[1], 1)
2301+
self.assertEqual(iis.row_index_[0], 1)
2302+
2303+
self.assertEqual(iis.col_bound_[0], highspy.IisBoundStatus.kIisBoundStatusLower)
2304+
self.assertEqual(iis.col_bound_[1], highspy.IisBoundStatus.kIisBoundStatusUpper)
2305+
self.assertEqual(iis.row_bound_[0], highspy.IisBoundStatus.kIisBoundStatusUpper)
2306+
2307+
self.assertEqual(iis.col_status_[0], highspy.IisStatus.kIisStatusInConflict)
2308+
self.assertEqual(iis.col_status_[1], highspy.IisStatus.kIisStatusInConflict)
2309+
self.assertEqual(iis.row_status_[0], highspy.IisStatus.kIisStatusNotInConflict)
2310+
self.assertEqual(iis.row_status_[1], highspy.IisStatus.kIisStatusInConflict)

0 commit comments

Comments
 (0)