Skip to content

Commit ae217a4

Browse files
authored
Merge pull request #3208 from ERGO-Code/fix-run-data
Fix HighsRunData
2 parents 005710c + 57e5065 commit ae217a4

2 files changed

Lines changed: 184 additions & 110 deletions

File tree

check/TestRunData.cpp

Lines changed: 179 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
1+
#include <cassert>
12
#include <cstdio>
23

34
#include "HCheckConfig.h"
45
#include "Highs.h"
56
#include "catch.hpp"
67
#include "io/HMPSIO.h"
78

8-
const bool dev_run = false;
9+
const bool dev_run = false; // true;//
10+
11+
const std::vector<std::string> solvers{
12+
// kHighsChooseString
13+
kSimplexString,
14+
// kIpxString,
15+
kHipoString
16+
// kQpAsmString
17+
// kHiPdlpString
18+
};
19+
20+
void testRunData(Highs& h, const bool irreducible, const bool reduces_to_empty,
21+
const std::string& run_data_file);
922

1023
TEST_CASE("run-data-md", "[highs_run_data]") {
1124
Highs h;
@@ -18,120 +31,182 @@ TEST_CASE("run-data-md", "[highs_run_data]") {
1831
}
1932

2033
TEST_CASE("highs-run-data", "[highs_run_data]") {
34+
// Doesn't work for MIPs yet, but wait until profiling is merged in
35+
// to avoid conflicts
36+
const std::vector<std::string> models{
37+
"adlittle", "egout-ac"
38+
// "flugpl"
39+
};
2140
const std::string test_name = Catch::getResultCapture().getCurrentTestName();
22-
const std::string highs_run_data_file = test_name + ".run_data";
41+
const std::string run_data_file = test_name + ".run_data";
2342

2443
Highs h;
25-
if (!dev_run) h.setOptionValue("output_flag", false);
26-
const HighsRunData& highs_run_data = h.getRunData();
27-
28-
auto testRunData = [&](const std::string& filename) {
44+
h.setOptionValue("output_flag", dev_run);
45+
const bool irreducible = false;
46+
for (auto& model : models) {
47+
std::string filename =
48+
std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
49+
REQUIRE(h.readModel(filename) == HighsStatus::kOk);
2950
HighsStatus return_status = h.readModel(filename);
3051
REQUIRE(return_status == HighsStatus::kOk);
52+
const bool reduces_to_empty = model == "egout-ac" ? true : false;
3153

32-
// Cannot write run_data since not valid before run()
33-
return_status = h.writeRunData("");
34-
REQUIRE(return_status == HighsStatus::kWarning);
35-
36-
HighsRunDataType highs_run_data_type;
37-
return_status = h.getRunDataType("presolved_num_col", highs_run_data_type);
38-
REQUIRE(return_status == HighsStatus::kError);
39-
return_status =
40-
h.getRunDataType("presolved_model_num_col", highs_run_data_type);
41-
REQUIRE(return_status == HighsStatus::kOk);
42-
REQUIRE(highs_run_data_type == HighsRunDataType::kInt);
43-
44-
return_status = h.getRunDataType("presolving_time", highs_run_data_type);
45-
REQUIRE(return_status == HighsStatus::kError);
46-
return_status = h.getRunDataType("presolve_time", highs_run_data_type);
47-
REQUIRE(return_status == HighsStatus::kOk);
48-
REQUIRE(highs_run_data_type == HighsRunDataType::kDouble);
49-
50-
// Run data not valid before run()
51-
HighsInt presolved_model_num_col;
52-
return_status =
53-
h.getRunDataValue("presolved_model_num_col", presolved_model_num_col);
54-
REQUIRE(return_status == HighsStatus::kWarning);
54+
for (auto& solver : solvers)
55+
testRunData(h, irreducible, reduces_to_empty, run_data_file);
56+
}
57+
if (!dev_run) std::remove(run_data_file.c_str());
5558

56-
return_status = h.run();
57-
REQUIRE(return_status == HighsStatus::kOk);
59+
h.resetGlobalScheduler(true);
60+
}
5861

59-
if (dev_run) {
60-
return_status = h.writeRunData("");
61-
REQUIRE(return_status == HighsStatus::kOk);
62+
TEST_CASE("highs-run-data-presolve", "[highs_run_data]") {
63+
const std::vector<std::string> models{"adlittle", "flugpl"};
64+
const std::string test_name = Catch::getResultCapture().getCurrentTestName();
65+
const std::string run_data_file = test_name + ".run_data";
66+
Highs h;
67+
h.setOptionValue("output_flag", dev_run);
68+
const HighsRunData& run_data = h.getRunData();
69+
const HighsLp& lp = h.getLp();
70+
for (auto& model : models) {
71+
std::string filename =
72+
std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
73+
REQUIRE(h.readModel(filename) == HighsStatus::kOk);
74+
const bool irreducible = true;
75+
const bool reduces_to_empty = false;
76+
for (auto& solver : solvers) {
77+
h.setOptionValue("solver", solver);
78+
if (dev_run)
79+
printf("\n!>>>>%s-%s<<<<\n", model.c_str(), solver.c_str());
80+
81+
REQUIRE(h.presolve() == HighsStatus::kOk);
82+
HighsLp presolved_lp = h.getPresolvedLp();
83+
84+
h.passModel(presolved_lp);
85+
h.setOptionValue("solve_relaxation", true);
86+
h.setOptionValue(kPresolveString, kHighsOffString);
87+
testRunData(h, irreducible, reduces_to_empty, run_data_file);
6288
}
89+
}
6390

64-
return_status = h.writeRunData(highs_run_data_file);
65-
REQUIRE(return_status == HighsStatus::kOk);
66-
67-
// Wrong name for objective
68-
return_status =
69-
h.getRunDataValue("presolved_num_col", presolved_model_num_col);
70-
REQUIRE(return_status == HighsStatus::kError);
71-
72-
// Right name for objective
73-
return_status =
74-
h.getRunDataValue("presolved_model_num_col", presolved_model_num_col);
75-
REQUIRE(return_status == HighsStatus::kOk);
76-
77-
if (dev_run)
78-
printf("From getRunDataValue: presolved_model_num_col = %d\n",
79-
int(presolved_model_num_col));
80-
81-
double presolve_time;
82-
// Wrong name for simplex iteration count
83-
return_status = h.getRunDataValue("presolving_time", presolve_time);
84-
REQUIRE(return_status == HighsStatus::kError);
91+
h.resetGlobalScheduler(true);
92+
}
8593

86-
// Right name for presolve time
87-
return_status = h.getRunDataValue("presolve_time", presolve_time);
94+
void testRunData(Highs& h, const bool irreducible, const bool reduces_to_empty,
95+
const std::string& run_data_file) {
96+
97+
assert(!(irreducible && reduces_to_empty));
98+
const HighsRunData& run_data = h.getRunData();
99+
const HighsLp& lp = h.getLp();
100+
101+
std::string presolve;
102+
h.getOptionValue(kPresolveString, presolve);
103+
const bool run_presolve = presolve != kHighsOffString;
104+
105+
// Cannot write run_data since not valid before run()
106+
HighsStatus return_status = h.writeRunData("");
107+
REQUIRE(return_status == HighsStatus::kWarning);
108+
109+
HighsRunDataType run_data_type;
110+
return_status = h.getRunDataType("presolved_num_col", run_data_type);
111+
REQUIRE(return_status == HighsStatus::kError);
112+
return_status = h.getRunDataType("presolved_model_num_col", run_data_type);
113+
REQUIRE(return_status == HighsStatus::kOk);
114+
REQUIRE(run_data_type == HighsRunDataType::kInt);
115+
116+
return_status = h.getRunDataType("presolving_time", run_data_type);
117+
REQUIRE(return_status == HighsStatus::kError);
118+
return_status = h.getRunDataType("presolve_time", run_data_type);
119+
REQUIRE(return_status == HighsStatus::kOk);
120+
REQUIRE(run_data_type == HighsRunDataType::kDouble);
121+
122+
// Run data not valid before run()
123+
HighsInt presolved_model_num_col;
124+
return_status =
125+
h.getRunDataValue("presolved_model_num_col", presolved_model_num_col);
126+
REQUIRE(return_status == HighsStatus::kWarning);
127+
128+
return_status = h.run();
129+
REQUIRE(return_status == HighsStatus::kOk);
130+
131+
if (dev_run) {
132+
return_status = h.writeRunData("");
88133
REQUIRE(return_status == HighsStatus::kOk);
89-
90-
const HighsModelStatus model_status = h.getModelStatus();
91-
if (dev_run) {
92-
printf("From getModelStatus: model_status = %s\n",
93-
h.modelStatusToString(model_status).c_str());
94-
printf("From getRunData: presolved_model_num_col = %d\n",
95-
int(highs_run_data.presolved_model_num_col));
96-
printf("From getRunData: presolved_model_num_row = %d\n",
97-
int(highs_run_data.presolved_model_num_row));
98-
printf("From getRunData: presolved_model_num_nz = %d\n",
99-
int(highs_run_data.presolved_model_num_nz));
100-
if (!h.getLp().isMip())
101-
printf(
102-
"From getRunData: num_simplex_iterations_after_postsolve = %d\n",
103-
int(highs_run_data.num_simplex_iterations_after_postsolve));
104-
printf("From getRunData: presolve_time = %g\n",
105-
highs_run_data.presolve_time);
106-
printf("From getRunData: solve_time = %g\n",
107-
highs_run_data.solve_time);
108-
printf("From getRunData: postsolve_time = %g\n",
109-
highs_run_data.postsolve_time);
110-
}
111-
REQUIRE(highs_run_data.presolved_model_num_col >= 0);
112-
REQUIRE(highs_run_data.presolved_model_num_row >= 0);
113-
REQUIRE(highs_run_data.presolved_model_num_nz >= 0);
134+
}
135+
136+
return_status = h.writeRunData(run_data_file);
137+
REQUIRE(return_status == HighsStatus::kOk);
138+
139+
// Wrong name for objective
140+
return_status =
141+
h.getRunDataValue("presolved_num_col", presolved_model_num_col);
142+
REQUIRE(return_status == HighsStatus::kError);
143+
144+
// Right name for objective
145+
return_status =
146+
h.getRunDataValue("presolved_model_num_col", presolved_model_num_col);
147+
REQUIRE(return_status == HighsStatus::kOk);
148+
149+
if (dev_run)
150+
printf("From getRunDataValue: presolved_model_num_col = %d\n",
151+
int(presolved_model_num_col));
152+
153+
double presolve_time;
154+
// Wrong name for presolve_time
155+
return_status = h.getRunDataValue("presolving_time", presolve_time);
156+
REQUIRE(return_status == HighsStatus::kError);
157+
158+
// Right name for presolve time
159+
return_status = h.getRunDataValue("presolve_time", presolve_time);
160+
REQUIRE(return_status == HighsStatus::kOk);
161+
162+
const HighsModelStatus model_status = h.getModelStatus();
163+
if (dev_run) {
164+
printf("From getModelStatus: model_status = %s\n",
165+
h.modelStatusToString(model_status).c_str());
166+
printf("From getRunData: presolved_model_num_col = %d\n",
167+
int(run_data.presolved_model_num_col));
168+
printf("From getRunData: presolved_model_num_row = %d\n",
169+
int(run_data.presolved_model_num_row));
170+
printf("From getRunData: presolved_model_num_nz = %d\n",
171+
int(run_data.presolved_model_num_nz));
114172
if (!h.getLp().isMip())
115-
REQUIRE(highs_run_data.num_simplex_iterations_after_postsolve == 0);
116-
REQUIRE(highs_run_data.presolve_time >= 0);
117-
REQUIRE(highs_run_data.solve_time >= 0);
118-
REQUIRE(highs_run_data.postsolve_time >= 0);
119-
};
120-
121-
std::string filename;
122-
filename = std::string(HIGHS_DIR) + "/check/instances/adlittle.mps";
123-
testRunData(filename);
124-
125-
filename = std::string(HIGHS_DIR) + "/check/instances/egout-ac.mps";
126-
testRunData(filename);
127-
128-
// Doesn't work for MIPs yet, but wait until profiling is merged in
129-
// to avoid conflicts
130-
//
131-
// filename = std::string(HIGHS_DIR) + "/check/instances/flugpl.mps";
132-
// testRunData(filename);
133-
134-
if (!dev_run) std::remove(highs_run_data_file.c_str());
135-
136-
h.resetGlobalScheduler(true);
173+
printf("From getRunData: num_simplex_iterations_after_postsolve = %d\n",
174+
int(run_data.num_simplex_iterations_after_postsolve));
175+
printf("From getRunData: presolve_time = %g\n", run_data.presolve_time);
176+
printf("From getRunData: solve_time = %g\n", run_data.solve_time);
177+
printf("From getRunData: postsolve_time = %g\n", run_data.postsolve_time);
178+
}
179+
if (run_presolve) {
180+
REQUIRE(run_data.presolve_time >= 0);
181+
REQUIRE(run_data.presolve_time < kHighsInf);
182+
REQUIRE(run_data.presolved_model_num_col >= 0);
183+
REQUIRE(run_data.presolved_model_num_row >= 0);
184+
REQUIRE(run_data.presolved_model_num_nz >= 0);
185+
if (!irreducible) {
186+
REQUIRE(run_data.presolved_model_num_col < lp.num_col_);
187+
REQUIRE(run_data.presolved_model_num_row < lp.num_row_);
188+
REQUIRE(run_data.presolved_model_num_nz < lp.a_matrix_.numNz());
189+
}
190+
if (reduces_to_empty) {
191+
REQUIRE(run_data.presolved_model_num_col == 0);
192+
REQUIRE(run_data.presolved_model_num_row == 0);
193+
REQUIRE(run_data.presolved_model_num_nz == 0);
194+
}
195+
REQUIRE(run_data.postsolve_time >= 0);
196+
REQUIRE(run_data.postsolve_time < kHighsInf);
197+
if (!h.getLp().isMip()) {
198+
REQUIRE(run_data.num_simplex_iterations_after_postsolve == 0);
199+
}
200+
} else {
201+
REQUIRE(run_data.presolve_time == kHighsIllegalDoubleMeasure);
202+
REQUIRE(run_data.presolved_model_num_col == kHighsIllegalIntMeasure);
203+
REQUIRE(run_data.presolved_model_num_row == kHighsIllegalIntMeasure);
204+
REQUIRE(run_data.presolved_model_num_nz == kHighsIllegalIntMeasure);
205+
REQUIRE(run_data.postsolve_time == kHighsIllegalDoubleMeasure);
206+
REQUIRE(run_data.num_simplex_iterations_after_postsolve ==
207+
kHighsIllegalIntMeasure);
208+
}
209+
REQUIRE(run_data.solve_time >= 0);
210+
REQUIRE(run_data.solve_time < kHighsInf);
211+
h.clearSolver();
137212
}

highs/lp_data/Highs.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,12 +1599,10 @@ HighsStatus Highs::calledOptimizeModel() {
15991599
if (basis_.valid) assert(basis_.useful);
16001600

16011601
const bool without_presolve = options_.presolve == kHighsOffString;
1602-
if ((unconstrained_lp || has_basis || without_presolve) &&
1603-
solver_will_use_basis) {
1604-
// There is a valid basis for the problem, presolve is off, or LP
1605-
// has no constraint matrix, and the solver will use the basis
1606-
// (otherwise it's better to use presolve, if it's not switched
1607-
// off)
1602+
if ((has_basis && solver_will_use_basis) || without_presolve ||
1603+
unconstrained_lp) {
1604+
// There is a valid basis for the problem and the solver will use
1605+
// it, or presolve is off, or LP has no constraint matrix
16081606
//
16091607
// Determine a coherent message about how the LP is being solved
16101608
std::stringstream lp_solve_ss;
@@ -1630,6 +1628,7 @@ HighsStatus Highs::calledOptimizeModel() {
16301628
timedSolveLp(incumbent_lp, lp_solve, this_solve_original_lp_time);
16311629
return_status = interpretCallStatus(options_.log_options, call_status,
16321630
return_status, "callSolveLp");
1631+
this->run_data_.solve_time = this_solve_original_lp_time;
16331632
if (return_status == HighsStatus::kError)
16341633
return returnFromOptimizeModel(return_status, undo_mods);
16351634
} else {

0 commit comments

Comments
 (0)