Skip to content

Commit 7cf807d

Browse files
committed
Merge branch 'latest' of https://github.com/ERGO-Code/HiGHS into fix-2290
2 parents 24c8177 + 5b9fa73 commit 7cf807d

File tree

8 files changed

+306
-348
lines changed

8 files changed

+306
-348
lines changed

FEATURES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ Added `Highs_changeRowsBoundsByRange` to C API, fixing [#2296](https://github.co
2020

2121
Corrected docstrings for `Highs_getReducedRow`, motivated by [#2312](https://github.com/ERGO-Code/HiGHS/issues/2312))
2222

23+
LP file reader no longer fails when there is no objective section. Fix is [#2316](https://github.com/ERGO-Code/HiGHS/pull/2316)), but this exposes code quality issue [#2318](https://github.com/ERGO-Code/HiGHS/issues/2318))
24+
25+
26+

check/TestFilereader.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,19 @@ TEST_CASE("filereader-edge-cases", "[highs_filereader]") {
9898
}
9999

100100
if (test_garbage_lp) {
101+
// Since #2316, reading an LP file of garbage yields an empty
102+
// model, since the absence of an objecive is (rightly) no
103+
// longer an error. However the LP file reader should fail due
104+
// to the requirement that a LP format file must begin with a
105+
// keyword.
101106
if (dev_run) printf("\ngarbage.lp\n");
102107
model_file = std::string(HIGHS_DIR) + "/check/instances/" + model + ".lp";
103108
read_status = highs.readModel(model_file);
104-
REQUIRE(read_status == HighsStatus::kError);
109+
// Should be HighsStatus::kError); #2316
110+
REQUIRE(read_status == HighsStatus::kOk);
111+
REQUIRE(highs.getLp().num_col_ == 0);
112+
REQUIRE(highs.getLp().num_row_ == 0);
113+
REQUIRE(highs.getLp().a_matrix_.numNz() == 0);
105114
}
106115
}
107116

@@ -110,7 +119,8 @@ TEST_CASE("filereader-edge-cases", "[highs_filereader]") {
110119
if (dev_run) printf("\n%s.mps\n", model.c_str());
111120
model_file = std::string(HIGHS_DIR) + "/check/instances/" + model + ".lp";
112121
read_status = highs.readModel(model_file);
113-
REQUIRE(read_status == HighsStatus::kError);
122+
// Should be HighsStatus::kError); #2316
123+
REQUIRE(read_status == HighsStatus::kOk);
114124

115125
// Gurobi cannot read
116126
//

check/TestHighsParallel.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ TEST_CASE("MatrixMultHighs", "[parallel]") {
224224
parallel::initialize_scheduler(numThreads);
225225
if (dev_run) std::cout << "\nhighs workstealing for loop:" << std::endl;
226226
matrix_multiplication("highs", parallel::num_threads(), 1);
227+
HighsTaskExecutor::shutdown();
227228
}
228229

229230
TEST_CASE("FibonacciTasksHighs", "[parallel]") {
@@ -245,6 +246,7 @@ TEST_CASE("FibonacciTasksHighs", "[parallel]") {
245246
// REQUIRE(result == 20365011074);
246247
// fib 41
247248
REQUIRE(result == 267914296);
249+
HighsTaskExecutor::shutdown();
248250
}
249251

250252
#if 0

check/TestMipSolver.cpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ void solve(Highs& highs, std::string presolve,
1616
const double require_iteration_count = -1);
1717
void distillationMIP(Highs& highs);
1818
void rowlessMIP(Highs& highs);
19+
void rowlessMIP1(Highs& highs);
20+
void rowlessMIP2(Highs& highs);
1921

2022
TEST_CASE("MIP-distillation", "[highs_test_mip_solver]") {
2123
Highs highs;
@@ -25,10 +27,25 @@ TEST_CASE("MIP-distillation", "[highs_test_mip_solver]") {
2527
highs.resetGlobalScheduler(true);
2628
}
2729

28-
TEST_CASE("MIP-rowless", "[highs_test_mip_solver]") {
30+
// Fails but the cases work separately in
31+
// MIP-rowless-1 and
32+
// MIP-rowless-2 below
33+
// TEST_CASE("MIP-rowless", "[highs_test_mip_solver]") {
34+
// Highs highs;
35+
// if (!dev_run) highs.setOptionValue("output_flag", false);
36+
// rowlessMIP(highs);
37+
// }
38+
39+
TEST_CASE("MIP-rowless-1", "[highs_test_mip_solver]") {
40+
Highs highs;
41+
if (!dev_run) highs.setOptionValue("output_flag", false);
42+
rowlessMIP1(highs);
43+
}
44+
45+
TEST_CASE("MIP-rowless-2", "[highs_test_mip_solver]") {
2946
Highs highs;
3047
if (!dev_run) highs.setOptionValue("output_flag", false);
31-
rowlessMIP(highs);
48+
rowlessMIP2(highs);
3249
}
3350

3451
TEST_CASE("MIP-solution-limit", "[highs_test_mip_solver]") {
@@ -805,6 +822,51 @@ void rowlessMIP(Highs& highs) {
805822
solve(highs, kHighsOffString, require_model_status, optimal_objective);
806823
}
807824

825+
void rowlessMIP1(Highs& highs) {
826+
HighsLp lp;
827+
HighsModelStatus require_model_status;
828+
double optimal_objective;
829+
lp.num_col_ = 2;
830+
lp.num_row_ = 0;
831+
lp.col_cost_ = {1, -1};
832+
lp.col_lower_ = {0, 0};
833+
lp.col_upper_ = {1, 1};
834+
lp.a_matrix_.start_ = {0, 0, 0};
835+
lp.a_matrix_.format_ = MatrixFormat::kColwise;
836+
lp.sense_ = ObjSense::kMinimize;
837+
lp.offset_ = 0;
838+
lp.integrality_ = {HighsVarType::kInteger, HighsVarType::kInteger};
839+
require_model_status = HighsModelStatus::kOptimal;
840+
optimal_objective = -1.0;
841+
REQUIRE(highs.passModel(lp) == HighsStatus::kOk);
842+
// Presolve reduces the LP to empty
843+
solve(highs, kHighsOnString, require_model_status, optimal_objective);
844+
// solve(highs, kHighsOffString, require_model_status, optimal_objective);
845+
}
846+
847+
848+
void rowlessMIP2(Highs& highs) {
849+
HighsLp lp;
850+
HighsModelStatus require_model_status;
851+
double optimal_objective;
852+
lp.num_col_ = 2;
853+
lp.num_row_ = 0;
854+
lp.col_cost_ = {1, -1};
855+
lp.col_lower_ = {0, 0};
856+
lp.col_upper_ = {1, 1};
857+
lp.a_matrix_.start_ = {0, 0, 0};
858+
lp.a_matrix_.format_ = MatrixFormat::kColwise;
859+
lp.sense_ = ObjSense::kMinimize;
860+
lp.offset_ = 0;
861+
lp.integrality_ = {HighsVarType::kInteger, HighsVarType::kInteger};
862+
require_model_status = HighsModelStatus::kOptimal;
863+
optimal_objective = -1.0;
864+
REQUIRE(highs.passModel(lp) == HighsStatus::kOk);
865+
// Presolve reduces the LP to empty
866+
// solve(highs, kHighsOnString, require_model_status, optimal_objective);
867+
solve(highs, kHighsOffString, require_model_status, optimal_objective);
868+
}
869+
808870
TEST_CASE("issue-2122", "[highs_test_mip_solver]") {
809871
std::string filename = std::string(HIGHS_DIR) + "/check/instances/2122.lp";
810872
Highs highs;

extern/filereaderlp/reader.cpp

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -308,23 +308,6 @@ Model Reader::read() {
308308
splittokens();
309309

310310
// std::clog << "Setting up model..." << std::endl;
311-
//
312-
// Since
313-
//
314-
// "The problem statement must begin with the word MINIMIZE or
315-
// MAXIMIZE, MINIMUM or MAXIMUM, or the abbreviations MIN or MAX, in
316-
// any combination of upper- and lower-case characters. The word
317-
// introduces the objective function section."
318-
//
319-
// Use positivity of sectiontokens.count(LpSectionKeyword::OBJMIN) +
320-
// sectiontokens.count(LpSectionKeyword::OBJMAX) to identify garbage file
321-
//
322-
323-
const int num_objective_section =
324-
sectiontokens.count(LpSectionKeyword::OBJMIN) +
325-
sectiontokens.count(LpSectionKeyword::OBJMAX);
326-
lpassert(num_objective_section>0);
327-
328311
processsections();
329312
processedtokens.clear();
330313
processedtokens.shrink_to_fit();

highs/presolve/HPresolve.cpp

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3774,28 +3774,41 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
37743774
}
37753775

37763776
auto strengthenCoefs = [&](HighsCDouble& rhs, HighsInt direction,
3777-
double maxCoefValue) {
3777+
HighsCDouble maxAbsCoefValue) {
37783778
// iterate over non-zero positions instead of iterating over the
37793779
// HighsMatrixSlice (provided by HPresolve::getStoredRow) because the
37803780
// latter contains pointers to Acol and Avalue that may be invalidated
37813781
// if these vectors are reallocated (see std::vector::push_back
37823782
// performed in HPresolve::addToMatrix).
37833783
for (HighsInt rowiter : rowpositions) {
3784+
// max. absolute coefficient should not be negative
3785+
assert(maxAbsCoefValue >= 0);
3786+
37843787
// get column index and coefficient
37853788
HighsInt col = Acol[rowiter];
3786-
double val = Avalue[rowiter];
3789+
double val = direction * Avalue[rowiter];
3790+
3791+
// get lower and upper bounds
3792+
double col_lower = impliedRowBounds.getImplVarLower(row, col);
3793+
double col_upper = impliedRowBounds.getImplVarUpper(row, col);
37873794

37883795
// skip continuous variables
37893796
if (model->integrality_[col] == HighsVarType::kContinuous) continue;
37903797

3791-
if (direction * val > maxCoefValue + primal_feastol) {
3792-
double delta = direction * maxCoefValue - val;
3793-
addToMatrix(row, col, delta);
3794-
rhs += delta * model->col_upper_[col];
3795-
} else if (direction * val < -maxCoefValue - primal_feastol) {
3796-
double delta = -direction * maxCoefValue - val;
3797-
addToMatrix(row, col, delta);
3798-
rhs += delta * model->col_lower_[col];
3798+
if (val > maxAbsCoefValue + primal_feastol) {
3799+
assert(col_upper != kHighsInf);
3800+
// new matrix coefficient is direction * maxAbsCoefValue; subtract
3801+
// existing matrix coefficient to get delta
3802+
HighsCDouble delta = direction * (maxAbsCoefValue - val);
3803+
addToMatrix(row, col, static_cast<double>(delta));
3804+
rhs += delta * col_upper;
3805+
} else if (val < -maxAbsCoefValue - primal_feastol) {
3806+
assert(col_lower != -kHighsInf);
3807+
// new matrix coefficient is (-direction) * maxAbsCoefValue;
3808+
// subtract existing matrix coefficient to get delta
3809+
HighsCDouble delta = -direction * (maxAbsCoefValue + val);
3810+
addToMatrix(row, col, static_cast<double>(delta));
3811+
rhs += delta * col_lower;
37993812
}
38003813
}
38013814
};
@@ -3805,7 +3818,8 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
38053818
// <= constraint: try to strengthen coefficients
38063819
HighsCDouble rhs = model->row_upper_[row];
38073820
strengthenCoefs(rhs, HighsInt{1},
3808-
impliedRowUpper - model->row_upper_[row]);
3821+
static_cast<HighsCDouble>(impliedRowUpper) -
3822+
model->row_upper_[row]);
38093823
model->row_upper_[row] = static_cast<double>(rhs);
38103824
}
38113825

@@ -3814,7 +3828,8 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
38143828
// >= constraint: try to strengthen coefficients
38153829
HighsCDouble rhs = model->row_lower_[row];
38163830
strengthenCoefs(rhs, HighsInt{-1},
3817-
model->row_lower_[row] - impliedRowLower);
3831+
model->row_lower_[row] -
3832+
static_cast<HighsCDouble>(impliedRowLower));
38183833
model->row_lower_[row] = static_cast<double>(rhs);
38193834
}
38203835
}

0 commit comments

Comments
 (0)