Skip to content

Commit d6b8feb

Browse files
committed
Test C API exploses IIS creation bug
1 parent d04c148 commit d6b8feb

File tree

4 files changed

+172
-73
lines changed

4 files changed

+172
-73
lines changed

check/TestCAPI.c

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,20 +2151,6 @@ void testIis() {
21512151
ret = Highs_addRow(highs, -inf, 1.0, 3, index, value_3);
21522152
assert(ret == 0);
21532153

2154-
HighsInt iis_num_col;
2155-
HighsInt iis_num_row;
2156-
ret = Highs_getIis(highs,
2157-
&iis_num_col, &iis_num_row,
2158-
NULL, NULL,
2159-
NULL, NULL,
2160-
NULL, NULL);
2161-
assert(ret == 0);
2162-
2163-
HighsInt* col_index = (HighsInt*)malloc(sizeof(HighsInt) * iis_num_col);
2164-
HighsInt* row_index = (HighsInt*)malloc(sizeof(HighsInt) * iis_num_row);
2165-
HighsInt* col_bound = (HighsInt*)malloc(sizeof(HighsInt) * iis_num_col);
2166-
HighsInt* row_bound = (HighsInt*)malloc(sizeof(HighsInt) * iis_num_row);
2167-
21682154
HighsInt num_col;
21692155
HighsInt num_row;
21702156
HighsInt num_nz;
@@ -2177,7 +2163,46 @@ void testIis() {
21772163
NULL, NULL,
21782164
NULL, NULL, NULL,
21792165
NULL);
2180-
Highs_run(highs);
2166+
2167+
for (int k = 0 ; k < 2; k++) {
2168+
HighsInt iis_num_col;
2169+
HighsInt iis_num_row;
2170+
ret = Highs_getIis(highs,
2171+
&iis_num_col, &iis_num_row,
2172+
NULL, NULL,
2173+
NULL, NULL,
2174+
NULL, NULL);
2175+
assert(ret == 0);
2176+
2177+
if (k == 0) {
2178+
// No IIS from kHighsIisStrategyLight
2179+
assert(iis_num_col == 0);
2180+
assert(iis_num_row == 0);
2181+
Highs_setIntOptionValue(highs, "iis_strategy",
2182+
kHighsIisStrategyFromLpRowPriority);
2183+
} else {
2184+
assert(iis_num_col == 2);
2185+
assert(iis_num_row == 2);
2186+
HighsInt* col_index = (HighsInt*)malloc(sizeof(HighsInt) * iis_num_col);
2187+
HighsInt* row_index = (HighsInt*)malloc(sizeof(HighsInt) * iis_num_row);
2188+
HighsInt* col_bound = (HighsInt*)malloc(sizeof(HighsInt) * iis_num_col);
2189+
HighsInt* row_bound = (HighsInt*)malloc(sizeof(HighsInt) * iis_num_row);
2190+
HighsInt* col_status = (HighsInt*)malloc(sizeof(HighsInt) * num_col);
2191+
HighsInt* row_status = (HighsInt*)malloc(sizeof(HighsInt) * num_row);
2192+
ret = Highs_getIis(highs,
2193+
&iis_num_col, &iis_num_row,
2194+
col_index, row_index,
2195+
col_bound, row_bound,
2196+
col_status, row_status);
2197+
assert(ret == 0);
2198+
free(col_index);
2199+
free(row_index);
2200+
free(col_bound);
2201+
free(row_bound);
2202+
free(col_status);
2203+
free(row_status);
2204+
}
2205+
}
21812206

21822207
Highs_destroy(highs);
21832208
}

highs/interfaces/highs_c_api.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,7 @@ HighsInt Highs_getIis(void* highs,
13441344
row_status[i] = iis.row_status_[i];
13451345
}
13461346
}
1347+
return status;
13471348
}
13481349

13491350
HighsInt Highs_getIisLp(const void* highs, const HighsInt a_format,

highs/interfaces/highs_c_api.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ const char* const kHighsCallbackDataOutCutpoolLowerName = "cutpool_lower";
134134
const char* const kHighsCallbackDataOutCutpoolUpperName = "cutpool_upper";
135135

136136
const HighsInt kHighsIisStrategyLight = 0;
137-
// const HighsInt kHighsIisStrategyFromLpRowPriority = 1; // WIP
138-
// const HighsInt kHighsIisStrategyFromLpColPriority = 2; // WIP
137+
const HighsInt kHighsIisStrategyFromLpRowPriority = 1; // WIP
138+
const HighsInt kHighsIisStrategyFromLpColPriority = 2; // WIP
139139

140140
const HighsInt kHighsIisStatusInConflict = 0;
141141
const HighsInt kHighsIisStatusNotInConflict = 1;

highs/lp_data/HighsIis.cpp

Lines changed: 129 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -348,16 +348,26 @@ void HighsIis::getLp(const HighsLp& lp) {
348348
iis_lp.clear();
349349
HighsInt iis_num_col = this->col_index_.size();
350350
HighsInt iis_num_row = this->row_index_.size();
351-
assert(lp.a_matrix_.isColwise());
352-
// Scatter the IIS rows into a full-length vector to identify IIS
353-
// rows with LP rows
351+
const bool colwise = lp.a_matrix_.isColwise();
352+
// Scatter the IIS rows (cols) into a full-length vector to identify
353+
// IIS rows (cols) with LP rows (cols) according to whether the
354+
// incumbent matrix is col-wise or row-wise
354355
std::vector<HighsInt> iis_row;
355-
iis_row.assign(lp.num_row_, -1);
356+
std::vector<HighsInt> iis_col;
357+
if (colwise) {
358+
iis_row.assign(lp.num_row_, -1);
359+
for (HighsInt iisRow = 0; iisRow < iis_num_row; iisRow++)
360+
iis_row[this->row_index_[iisRow]] = iisRow;
361+
} else {
362+
iis_col.assign(lp.num_col_, -1);
363+
for (HighsInt iisCol = 0; iisCol < iis_num_col; iisCol++)
364+
iis_col[this->col_index_[iisCol]] = iisCol;
365+
}
356366
double bound;
367+
357368
const bool has_row_name = lp.row_names_.size() > 0;
358369
for (HighsInt iisRow = 0; iisRow < iis_num_row; iisRow++) {
359370
HighsInt iRow = this->row_index_[iisRow];
360-
iis_row[iRow] = iisRow;
361371
if (has_row_name) iis_lp.row_names_.push_back(lp.row_names_[iRow]);
362372
HighsInt row_bound = this->row_bound_[iisRow];
363373
assert(row_bound == kIisBoundStatusLower ||
@@ -373,6 +383,17 @@ void HighsIis::getLp(const HighsLp& lp) {
373383
? lp.row_upper_[iRow]
374384
: kHighsInf;
375385
iis_lp.row_upper_.push_back(bound);
386+
if (!colwise) {
387+
for (HighsInt iEl = lp.a_matrix_.start_[iRow];
388+
iEl < lp.a_matrix_.start_[iRow + 1]; iEl++) {
389+
HighsInt iCol = lp.a_matrix_.index_[iEl];
390+
HighsInt iisCol = iis_col[iCol];
391+
if (iisCol >= 0) {
392+
iis_lp.a_matrix_.index_.push_back(iisCol);
393+
iis_lp.a_matrix_.value_.push_back(lp.a_matrix_.value_[iEl]);
394+
}
395+
}
396+
}
376397
}
377398

378399
const bool has_col_name = lp.col_names_.size() > 0;
@@ -398,13 +419,15 @@ void HighsIis::getLp(const HighsLp& lp) {
398419
? lp.col_upper_[iCol]
399420
: kHighsInf;
400421
iis_lp.col_upper_.push_back(bound);
401-
for (HighsInt iEl = lp.a_matrix_.start_[iCol];
402-
iEl < lp.a_matrix_.start_[iCol + 1]; iEl++) {
403-
HighsInt iRow = lp.a_matrix_.index_[iEl];
404-
HighsInt iisRow = iis_row[iRow];
405-
if (iisRow >= 0) {
406-
iis_lp.a_matrix_.index_.push_back(iisRow);
407-
iis_lp.a_matrix_.value_.push_back(lp.a_matrix_.value_[iEl]);
422+
if (colwise) {
423+
for (HighsInt iEl = lp.a_matrix_.start_[iCol];
424+
iEl < lp.a_matrix_.start_[iCol + 1]; iEl++) {
425+
HighsInt iRow = lp.a_matrix_.index_[iEl];
426+
HighsInt iisRow = iis_row[iRow];
427+
if (iisRow >= 0) {
428+
iis_lp.a_matrix_.index_.push_back(iisRow);
429+
iis_lp.a_matrix_.value_.push_back(lp.a_matrix_.value_[iEl]);
430+
}
408431
}
409432
}
410433
iis_lp.a_matrix_.start_.push_back(iis_lp.a_matrix_.index_.size());
@@ -759,16 +782,13 @@ bool HighsIis::lpDataOk(const HighsLp& lp, const HighsOptions& options) const {
759782
if (!(iis_lp.num_col_ == iis_num_col)) return false;
760783
if (!(iis_lp.num_row_ == iis_num_row)) return false;
761784

762-
assert(lp.a_matrix_.isColwise());
785+
const bool colwise = lp.a_matrix_.isColwise();
763786

764787
std::vector<HighsInt> iis_row;
765788
iis_row.assign(lp.num_row_, -1);
766789
double bound;
767790
for (HighsInt iisRow = 0; iisRow < iis_num_row; iisRow++) {
768791
HighsInt iRow = this->row_index_[iisRow];
769-
if (iRow < 0 || iRow >= lp.num_row_) {
770-
printf("iRow out of range\n");
771-
}
772792
iis_row[iRow] = iisRow;
773793
HighsInt row_bound = this->row_bound_[iisRow];
774794
bound =
@@ -783,12 +803,7 @@ bool HighsIis::lpDataOk(const HighsLp& lp, const HighsOptions& options) const {
783803
if (iis_lp.row_upper_[iisRow] != bound) return false;
784804
}
785805

786-
// Work through the LP columns and matrix, checking the zero costs,
787-
// bounds and matrix index/value
788-
const HighsInt illegal_index = -1;
789-
const double illegal_value = kHighsInf;
790-
std::vector<HighsInt> index;
791-
std::vector<double> value;
806+
// Work through the LP columns checking the zero costs and bounds
792807
for (HighsInt iisCol = 0; iisCol < iis_num_col; iisCol++) {
793808
HighsInt iCol = this->col_index_[iisCol];
794809
if (iis_lp.col_cost_[iisCol]) return false;
@@ -803,48 +818,106 @@ bool HighsIis::lpDataOk(const HighsLp& lp, const HighsOptions& options) const {
803818
? lp.col_upper_[iCol]
804819
: kHighsInf;
805820
if (iis_lp.col_upper_[iisCol] != bound) return false;
806-
// Use index/value to scatter the IIS matrix column
807-
index.assign(iis_num_row, illegal_index);
808-
value.assign(iis_num_row, illegal_value);
809-
for (HighsInt iEl = iis_lp.a_matrix_.start_[iisCol];
810-
iEl < iis_lp.a_matrix_.start_[iisCol + 1]; iEl++) {
811-
HighsInt iisRow = iis_lp.a_matrix_.index_[iEl];
812-
HighsInt iRow = this->row_index_[iisRow];
813-
index[iisRow] = iRow;
814-
value[iisRow] = iis_lp.a_matrix_.value_[iEl];
821+
}
822+
const HighsInt illegal_index = -1;
823+
const double illegal_value = kHighsInf;
824+
std::vector<HighsInt> index;
825+
std::vector<double> value;
826+
// Work through the LP matrix, checking the matrix index/value
827+
if (colwise) {
828+
for (HighsInt iisCol = 0; iisCol < iis_num_col; iisCol++) {
829+
HighsInt iCol = this->col_index_[iisCol];
830+
// Use index/value to scatter the IIS matrix column
831+
index.assign(iis_num_row, illegal_index);
832+
value.assign(iis_num_row, illegal_value);
833+
for (HighsInt iEl = iis_lp.a_matrix_.start_[iisCol];
834+
iEl < iis_lp.a_matrix_.start_[iisCol + 1]; iEl++) {
835+
HighsInt iisRow = iis_lp.a_matrix_.index_[iEl];
836+
HighsInt iRow = this->row_index_[iisRow];
837+
index[iisRow] = iRow;
838+
value[iisRow] = iis_lp.a_matrix_.value_[iEl];
839+
}
840+
for (HighsInt iEl = lp.a_matrix_.start_[iCol];
841+
iEl < lp.a_matrix_.start_[iCol + 1]; iEl++) {
842+
HighsInt iRow = lp.a_matrix_.index_[iEl];
843+
HighsInt iisRow = iis_row[iRow];
844+
if (iisRow >= 0) {
845+
if (index[iisRow] != iRow) return false;
846+
if (value[iisRow] != lp.a_matrix_.value_[iEl]) return false;
847+
index[iisRow] = illegal_index;
848+
value[iisRow] = illegal_value;
849+
}
850+
}
815851
}
816-
for (HighsInt iEl = lp.a_matrix_.start_[iCol];
817-
iEl < lp.a_matrix_.start_[iCol + 1]; iEl++) {
818-
HighsInt iRow = lp.a_matrix_.index_[iEl];
819-
HighsInt iisRow = iis_row[iRow];
820-
if (iisRow >= 0) {
821-
if (index[iisRow] != iRow) return false;
822-
if (value[iisRow] != lp.a_matrix_.value_[iEl]) return false;
823-
index[iisRow] = illegal_index;
824-
value[iisRow] = illegal_value;
852+
} else {
853+
for (HighsInt iisRow = 0; iisRow < iis_num_row; iisRow++) {
854+
HighsInt iRow = this->row_index_[iisRow];
855+
// Use index/value to scatter the IIS matrix row
856+
index.assign(iis_num_row, illegal_index);
857+
value.assign(iis_num_row, illegal_value);
858+
for (HighsInt iEl = iis_lp.a_matrix_.start_[iisRow];
859+
iEl < iis_lp.a_matrix_.start_[iisRow + 1]; iEl++) {
860+
HighsInt iisCol = iis_lp.a_matrix_.index_[iEl];
861+
HighsInt iCol = this->row_index_[iisCol];
862+
index[iisCol] = iCol;
863+
value[iisCol] = iis_lp.a_matrix_.value_[iEl];
864+
}
865+
for (HighsInt iEl = lp.a_matrix_.start_[iRow];
866+
iEl < lp.a_matrix_.start_[iRow + 1]; iEl++) {
867+
HighsInt iCol = lp.a_matrix_.index_[iEl];
868+
HighsInt iisCol = iis_row[iCol];
869+
if (iisCol >= 0) {
870+
if (index[iisCol] != iCol) return false;
871+
if (value[iisCol] != lp.a_matrix_.value_[iEl]) return false;
872+
index[iisCol] = illegal_index;
873+
value[iisCol] = illegal_value;
874+
}
825875
}
826876
}
827877
}
828878
// Work through the IIS LP matrix, making sure that the index/value
829879
// are correct
830-
for (HighsInt iisCol = 0; iisCol < iis_num_col; iisCol++) {
831-
HighsInt iCol = this->col_index_[iisCol];
832-
// Use index/value to scatter the LP matrix column
833-
index.assign(lp.num_row_, illegal_index);
834-
value.assign(lp.num_row_, illegal_value);
835-
for (HighsInt iEl = lp.a_matrix_.start_[iCol];
836-
iEl < lp.a_matrix_.start_[iCol + 1]; iEl++) {
837-
HighsInt iRow = lp.a_matrix_.index_[iEl];
838-
HighsInt iisRow = iis_row[iRow];
839-
index[iRow] = iisRow;
840-
value[iRow] = lp.a_matrix_.value_[iEl];
880+
if (colwise) {
881+
for (HighsInt iisCol = 0; iisCol < iis_num_col; iisCol++) {
882+
HighsInt iCol = this->col_index_[iisCol];
883+
// Use index/value to scatter the LP matrix column
884+
index.assign(lp.num_row_, illegal_index);
885+
value.assign(lp.num_row_, illegal_value);
886+
for (HighsInt iEl = lp.a_matrix_.start_[iCol];
887+
iEl < lp.a_matrix_.start_[iCol + 1]; iEl++) {
888+
HighsInt iRow = lp.a_matrix_.index_[iEl];
889+
HighsInt iisRow = iis_row[iRow];
890+
index[iRow] = iisRow;
891+
value[iRow] = lp.a_matrix_.value_[iEl];
892+
}
893+
for (HighsInt iEl = iis_lp.a_matrix_.start_[iisCol];
894+
iEl < iis_lp.a_matrix_.start_[iisCol + 1]; iEl++) {
895+
HighsInt iisRow = iis_lp.a_matrix_.index_[iEl];
896+
HighsInt iRow = this->row_index_[iisRow];
897+
if (index[iRow] != iisRow) return false;
898+
if (value[iRow] != iis_lp.a_matrix_.value_[iEl]) return false;
899+
}
841900
}
842-
for (HighsInt iEl = iis_lp.a_matrix_.start_[iisCol];
843-
iEl < iis_lp.a_matrix_.start_[iisCol + 1]; iEl++) {
844-
HighsInt iisRow = iis_lp.a_matrix_.index_[iEl];
901+
} else {
902+
for (HighsInt iisRow = 0; iisRow < iis_num_row; iisRow++) {
845903
HighsInt iRow = this->row_index_[iisRow];
846-
if (index[iRow] != iisRow) return false;
847-
if (value[iRow] != iis_lp.a_matrix_.value_[iEl]) return false;
904+
// Use index/value to scatter the LP matrix row
905+
index.assign(lp.num_row_, illegal_index);
906+
value.assign(lp.num_row_, illegal_value);
907+
for (HighsInt iEl = lp.a_matrix_.start_[iRow];
908+
iEl < lp.a_matrix_.start_[iRow + 1]; iEl++) {
909+
HighsInt iCol = lp.a_matrix_.index_[iEl];
910+
HighsInt iisCol = iis_row[iCol];
911+
index[iCol] = iisCol;
912+
value[iCol] = lp.a_matrix_.value_[iEl];
913+
}
914+
for (HighsInt iEl = iis_lp.a_matrix_.start_[iisRow];
915+
iEl < iis_lp.a_matrix_.start_[iisRow + 1]; iEl++) {
916+
HighsInt iisCol = iis_lp.a_matrix_.index_[iEl];
917+
HighsInt iCol = this->row_index_[iisCol];
918+
if (index[iCol] != iisCol) return false;
919+
if (value[iCol] != iis_lp.a_matrix_.value_[iEl]) return false;
920+
}
848921
}
849922
}
850923
return true;

0 commit comments

Comments
 (0)