diff --git a/check/TestRunData.cpp b/check/TestRunData.cpp index 04f1e77cab3..c93de54a07e 100644 --- a/check/TestRunData.cpp +++ b/check/TestRunData.cpp @@ -1,3 +1,4 @@ +#include #include #include "HCheckConfig.h" @@ -5,7 +6,19 @@ #include "catch.hpp" #include "io/HMPSIO.h" -const bool dev_run = false; +const bool dev_run = false; // true;// + +const std::vector solvers{ + // kHighsChooseString + kSimplexString, + // kIpxString, + kHipoString + // kQpAsmString + // kHiPdlpString +}; + +void testRunData(Highs& h, const bool irreducible, const bool reduces_to_empty, + const std::string& run_data_file); TEST_CASE("run-data-md", "[highs_run_data]") { Highs h; @@ -18,120 +31,182 @@ TEST_CASE("run-data-md", "[highs_run_data]") { } TEST_CASE("highs-run-data", "[highs_run_data]") { + // Doesn't work for MIPs yet, but wait until profiling is merged in + // to avoid conflicts + const std::vector models{ + "adlittle", "egout-ac" + // "flugpl" + }; const std::string test_name = Catch::getResultCapture().getCurrentTestName(); - const std::string highs_run_data_file = test_name + ".run_data"; + const std::string run_data_file = test_name + ".run_data"; Highs h; - if (!dev_run) h.setOptionValue("output_flag", false); - const HighsRunData& highs_run_data = h.getRunData(); - - auto testRunData = [&](const std::string& filename) { + h.setOptionValue("output_flag", dev_run); + const bool irreducible = false; + for (auto& model : models) { + std::string filename = + std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; + REQUIRE(h.readModel(filename) == HighsStatus::kOk); HighsStatus return_status = h.readModel(filename); REQUIRE(return_status == HighsStatus::kOk); + const bool reduces_to_empty = model == "egout-ac" ? true : false; - // Cannot write run_data since not valid before run() - return_status = h.writeRunData(""); - REQUIRE(return_status == HighsStatus::kWarning); - - HighsRunDataType highs_run_data_type; - return_status = h.getRunDataType("presolved_num_col", highs_run_data_type); - REQUIRE(return_status == HighsStatus::kError); - return_status = - h.getRunDataType("presolved_model_num_col", highs_run_data_type); - REQUIRE(return_status == HighsStatus::kOk); - REQUIRE(highs_run_data_type == HighsRunDataType::kInt); - - return_status = h.getRunDataType("presolving_time", highs_run_data_type); - REQUIRE(return_status == HighsStatus::kError); - return_status = h.getRunDataType("presolve_time", highs_run_data_type); - REQUIRE(return_status == HighsStatus::kOk); - REQUIRE(highs_run_data_type == HighsRunDataType::kDouble); - - // Run data not valid before run() - HighsInt presolved_model_num_col; - return_status = - h.getRunDataValue("presolved_model_num_col", presolved_model_num_col); - REQUIRE(return_status == HighsStatus::kWarning); + for (auto& solver : solvers) + testRunData(h, irreducible, reduces_to_empty, run_data_file); + } + if (!dev_run) std::remove(run_data_file.c_str()); - return_status = h.run(); - REQUIRE(return_status == HighsStatus::kOk); + h.resetGlobalScheduler(true); +} - if (dev_run) { - return_status = h.writeRunData(""); - REQUIRE(return_status == HighsStatus::kOk); +TEST_CASE("highs-run-data-presolve", "[highs_run_data]") { + const std::vector models{"adlittle", "flugpl"}; + const std::string test_name = Catch::getResultCapture().getCurrentTestName(); + const std::string run_data_file = test_name + ".run_data"; + Highs h; + h.setOptionValue("output_flag", dev_run); + const HighsRunData& run_data = h.getRunData(); + const HighsLp& lp = h.getLp(); + for (auto& model : models) { + std::string filename = + std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; + REQUIRE(h.readModel(filename) == HighsStatus::kOk); + const bool irreducible = true; + const bool reduces_to_empty = false; + for (auto& solver : solvers) { + h.setOptionValue("solver", solver); + if (dev_run) + printf("\n!>>>>%s-%s<<<<\n", model.c_str(), solver.c_str()); + + REQUIRE(h.presolve() == HighsStatus::kOk); + HighsLp presolved_lp = h.getPresolvedLp(); + + h.passModel(presolved_lp); + h.setOptionValue("solve_relaxation", true); + h.setOptionValue(kPresolveString, kHighsOffString); + testRunData(h, irreducible, reduces_to_empty, run_data_file); } + } - return_status = h.writeRunData(highs_run_data_file); - REQUIRE(return_status == HighsStatus::kOk); - - // Wrong name for objective - return_status = - h.getRunDataValue("presolved_num_col", presolved_model_num_col); - REQUIRE(return_status == HighsStatus::kError); - - // Right name for objective - return_status = - h.getRunDataValue("presolved_model_num_col", presolved_model_num_col); - REQUIRE(return_status == HighsStatus::kOk); - - if (dev_run) - printf("From getRunDataValue: presolved_model_num_col = %d\n", - int(presolved_model_num_col)); - - double presolve_time; - // Wrong name for simplex iteration count - return_status = h.getRunDataValue("presolving_time", presolve_time); - REQUIRE(return_status == HighsStatus::kError); + h.resetGlobalScheduler(true); +} - // Right name for presolve time - return_status = h.getRunDataValue("presolve_time", presolve_time); +void testRunData(Highs& h, const bool irreducible, const bool reduces_to_empty, + const std::string& run_data_file) { + + assert(!(irreducible && reduces_to_empty)); + const HighsRunData& run_data = h.getRunData(); + const HighsLp& lp = h.getLp(); + + std::string presolve; + h.getOptionValue(kPresolveString, presolve); + const bool run_presolve = presolve != kHighsOffString; + + // Cannot write run_data since not valid before run() + HighsStatus return_status = h.writeRunData(""); + REQUIRE(return_status == HighsStatus::kWarning); + + HighsRunDataType run_data_type; + return_status = h.getRunDataType("presolved_num_col", run_data_type); + REQUIRE(return_status == HighsStatus::kError); + return_status = h.getRunDataType("presolved_model_num_col", run_data_type); + REQUIRE(return_status == HighsStatus::kOk); + REQUIRE(run_data_type == HighsRunDataType::kInt); + + return_status = h.getRunDataType("presolving_time", run_data_type); + REQUIRE(return_status == HighsStatus::kError); + return_status = h.getRunDataType("presolve_time", run_data_type); + REQUIRE(return_status == HighsStatus::kOk); + REQUIRE(run_data_type == HighsRunDataType::kDouble); + + // Run data not valid before run() + HighsInt presolved_model_num_col; + return_status = + h.getRunDataValue("presolved_model_num_col", presolved_model_num_col); + REQUIRE(return_status == HighsStatus::kWarning); + + return_status = h.run(); + REQUIRE(return_status == HighsStatus::kOk); + + if (dev_run) { + return_status = h.writeRunData(""); REQUIRE(return_status == HighsStatus::kOk); - - const HighsModelStatus model_status = h.getModelStatus(); - if (dev_run) { - printf("From getModelStatus: model_status = %s\n", - h.modelStatusToString(model_status).c_str()); - printf("From getRunData: presolved_model_num_col = %d\n", - int(highs_run_data.presolved_model_num_col)); - printf("From getRunData: presolved_model_num_row = %d\n", - int(highs_run_data.presolved_model_num_row)); - printf("From getRunData: presolved_model_num_nz = %d\n", - int(highs_run_data.presolved_model_num_nz)); - if (!h.getLp().isMip()) - printf( - "From getRunData: num_simplex_iterations_after_postsolve = %d\n", - int(highs_run_data.num_simplex_iterations_after_postsolve)); - printf("From getRunData: presolve_time = %g\n", - highs_run_data.presolve_time); - printf("From getRunData: solve_time = %g\n", - highs_run_data.solve_time); - printf("From getRunData: postsolve_time = %g\n", - highs_run_data.postsolve_time); - } - REQUIRE(highs_run_data.presolved_model_num_col >= 0); - REQUIRE(highs_run_data.presolved_model_num_row >= 0); - REQUIRE(highs_run_data.presolved_model_num_nz >= 0); + } + + return_status = h.writeRunData(run_data_file); + REQUIRE(return_status == HighsStatus::kOk); + + // Wrong name for objective + return_status = + h.getRunDataValue("presolved_num_col", presolved_model_num_col); + REQUIRE(return_status == HighsStatus::kError); + + // Right name for objective + return_status = + h.getRunDataValue("presolved_model_num_col", presolved_model_num_col); + REQUIRE(return_status == HighsStatus::kOk); + + if (dev_run) + printf("From getRunDataValue: presolved_model_num_col = %d\n", + int(presolved_model_num_col)); + + double presolve_time; + // Wrong name for presolve_time + return_status = h.getRunDataValue("presolving_time", presolve_time); + REQUIRE(return_status == HighsStatus::kError); + + // Right name for presolve time + return_status = h.getRunDataValue("presolve_time", presolve_time); + REQUIRE(return_status == HighsStatus::kOk); + + const HighsModelStatus model_status = h.getModelStatus(); + if (dev_run) { + printf("From getModelStatus: model_status = %s\n", + h.modelStatusToString(model_status).c_str()); + printf("From getRunData: presolved_model_num_col = %d\n", + int(run_data.presolved_model_num_col)); + printf("From getRunData: presolved_model_num_row = %d\n", + int(run_data.presolved_model_num_row)); + printf("From getRunData: presolved_model_num_nz = %d\n", + int(run_data.presolved_model_num_nz)); if (!h.getLp().isMip()) - REQUIRE(highs_run_data.num_simplex_iterations_after_postsolve == 0); - REQUIRE(highs_run_data.presolve_time >= 0); - REQUIRE(highs_run_data.solve_time >= 0); - REQUIRE(highs_run_data.postsolve_time >= 0); - }; - - std::string filename; - filename = std::string(HIGHS_DIR) + "/check/instances/adlittle.mps"; - testRunData(filename); - - filename = std::string(HIGHS_DIR) + "/check/instances/egout-ac.mps"; - testRunData(filename); - - // Doesn't work for MIPs yet, but wait until profiling is merged in - // to avoid conflicts - // - // filename = std::string(HIGHS_DIR) + "/check/instances/flugpl.mps"; - // testRunData(filename); - - if (!dev_run) std::remove(highs_run_data_file.c_str()); - - h.resetGlobalScheduler(true); + printf("From getRunData: num_simplex_iterations_after_postsolve = %d\n", + int(run_data.num_simplex_iterations_after_postsolve)); + printf("From getRunData: presolve_time = %g\n", run_data.presolve_time); + printf("From getRunData: solve_time = %g\n", run_data.solve_time); + printf("From getRunData: postsolve_time = %g\n", run_data.postsolve_time); + } + if (run_presolve) { + REQUIRE(run_data.presolve_time >= 0); + REQUIRE(run_data.presolve_time < kHighsInf); + REQUIRE(run_data.presolved_model_num_col >= 0); + REQUIRE(run_data.presolved_model_num_row >= 0); + REQUIRE(run_data.presolved_model_num_nz >= 0); + if (!irreducible) { + REQUIRE(run_data.presolved_model_num_col < lp.num_col_); + REQUIRE(run_data.presolved_model_num_row < lp.num_row_); + REQUIRE(run_data.presolved_model_num_nz < lp.a_matrix_.numNz()); + } + if (reduces_to_empty) { + REQUIRE(run_data.presolved_model_num_col == 0); + REQUIRE(run_data.presolved_model_num_row == 0); + REQUIRE(run_data.presolved_model_num_nz == 0); + } + REQUIRE(run_data.postsolve_time >= 0); + REQUIRE(run_data.postsolve_time < kHighsInf); + if (!h.getLp().isMip()) { + REQUIRE(run_data.num_simplex_iterations_after_postsolve == 0); + } + } else { + REQUIRE(run_data.presolve_time == kHighsIllegalDoubleMeasure); + REQUIRE(run_data.presolved_model_num_col == kHighsIllegalIntMeasure); + REQUIRE(run_data.presolved_model_num_row == kHighsIllegalIntMeasure); + REQUIRE(run_data.presolved_model_num_nz == kHighsIllegalIntMeasure); + REQUIRE(run_data.postsolve_time == kHighsIllegalDoubleMeasure); + REQUIRE(run_data.num_simplex_iterations_after_postsolve == + kHighsIllegalIntMeasure); + } + REQUIRE(run_data.solve_time >= 0); + REQUIRE(run_data.solve_time < kHighsInf); + h.clearSolver(); } diff --git a/highs/lp_data/Highs.cpp b/highs/lp_data/Highs.cpp index 9f379c359f3..df15e366791 100644 --- a/highs/lp_data/Highs.cpp +++ b/highs/lp_data/Highs.cpp @@ -1599,12 +1599,10 @@ HighsStatus Highs::calledOptimizeModel() { if (basis_.valid) assert(basis_.useful); const bool without_presolve = options_.presolve == kHighsOffString; - if ((unconstrained_lp || has_basis || without_presolve) && - solver_will_use_basis) { - // There is a valid basis for the problem, presolve is off, or LP - // has no constraint matrix, and the solver will use the basis - // (otherwise it's better to use presolve, if it's not switched - // off) + if ((has_basis && solver_will_use_basis) || without_presolve || + unconstrained_lp) { + // There is a valid basis for the problem and the solver will use + // it, or presolve is off, or LP has no constraint matrix // // Determine a coherent message about how the LP is being solved std::stringstream lp_solve_ss; @@ -1630,6 +1628,7 @@ HighsStatus Highs::calledOptimizeModel() { timedSolveLp(incumbent_lp, lp_solve, this_solve_original_lp_time); return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); + this->run_data_.solve_time = this_solve_original_lp_time; if (return_status == HighsStatus::kError) return returnFromOptimizeModel(return_status, undo_mods); } else {