From 5aba249d9e33115656da585131ed974720174aa5 Mon Sep 17 00:00:00 2001 From: jajhall Date: Wed, 11 Sep 2024 16:37:36 +0100 Subject: [PATCH] Methods receiving matrix data where only small values are explicit zeros (so removed internally) are now silent and return HighsStatus::kOk (since internal matrix is exact) --- FEATURES.md | 7 +++++++ check/TestLpModification.cpp | 17 +++++++++++++++++ check/TestLpValidation.cpp | 2 +- check/TestRays.cpp | 2 ++ src/lp_data/HighsSolve.cpp | 2 +- src/test/DevKkt.cpp | 4 ++-- src/util/HighsMatrixUtils.cpp | 19 ++++++++++++------- 7 files changed, 42 insertions(+), 11 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index f0a52fcf7f..170a933885 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -16,6 +16,13 @@ Added `Highs::feasibilityRelaxation` to solve the problem of minimizing a (possi Added Python utility `examples/plot_highs_log.py` (due to @Thell) to visualise progress of the MIP solver. +Added minimal documentation of solvers and how simplex variants can be run + +Methods receiving matrix data where only small values are explicit zeros (so removed internally) are now silent and return HighsStatus::kOk (since internal matrix is exact) + + + + diff --git a/check/TestLpModification.cpp b/check/TestLpModification.cpp index b34cf18cae..ea55d16264 100644 --- a/check/TestLpModification.cpp +++ b/check/TestLpModification.cpp @@ -1937,3 +1937,20 @@ TEST_CASE("modify-empty-model", "[highs_data]") { REQUIRE(highs.changeColBounds(0, 1, 1) == HighsStatus::kError); REQUIRE(highs.changeRowBounds(0, 1, 1) == HighsStatus::kError); } + +TEST_CASE("zero-matrix-entries", "[highs_data]") { + Highs highs; + highs.setOptionValue("output_flag", dev_run); + HighsLp lp; + lp.num_col_ = 2; + lp.num_row_ = 2; + lp.col_cost_ = {0, 0}; + lp.col_lower_ = {0, 0}; + lp.col_upper_ = {1, 1}; + lp.row_lower_ = {-kHighsInf, -kHighsInf}; + lp.row_upper_ = {5, 8}; + lp.a_matrix_.start_ = {0, 2, 4}; + lp.a_matrix_.index_ = {0, 1, 0, 1}; + lp.a_matrix_.value_ = {1, 0, 0, 1}; + REQUIRE(highs.passModel(lp) == HighsStatus::kOk); +} diff --git a/check/TestLpValidation.cpp b/check/TestLpValidation.cpp index bb0f71c1db..473fdeb681 100644 --- a/check/TestLpValidation.cpp +++ b/check/TestLpValidation.cpp @@ -94,7 +94,7 @@ TEST_CASE("LP-dimension-validation", "[highs_data]") { // Yields duplicate index, but values are still zero, so both are // discarded and a warning is returned lp.a_matrix_.index_[0] = 0; - REQUIRE(highs.passModel(lp) == HighsStatus::kWarning); + REQUIRE(highs.passModel(lp) == HighsStatus::kOk); if (dev_run) printf("Give nonzero a_matrix_.value_[0] and a_matrix_.value_[1]\n"); diff --git a/check/TestRays.cpp b/check/TestRays.cpp index 0b9f6ecfbf..5a42d19bc5 100644 --- a/check/TestRays.cpp +++ b/check/TestRays.cpp @@ -303,6 +303,8 @@ void testUnboundedMpsLp(const std::string model, // Test dual ray for unbounded LP model_file = std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; require_model_status = HighsModelStatus::kUnbounded; + // gas11 contains small nonzero matrix entries, so readModel yield + // HighsStatus::kWarning require_status = model == "gas11" ? HighsStatus::kWarning : HighsStatus::kOk; REQUIRE(highs.readModel(model_file) == require_status); REQUIRE(highs.changeObjectiveSense(sense) == HighsStatus::kOk); diff --git a/src/lp_data/HighsSolve.cpp b/src/lp_data/HighsSolve.cpp index d839bfb88d..b1a33476fb 100644 --- a/src/lp_data/HighsSolve.cpp +++ b/src/lp_data/HighsSolve.cpp @@ -127,7 +127,7 @@ HighsStatus solveLp(HighsLpSolverObject& solver_object, const string message) { return HighsStatus::kError; } } // options.run_crossover == kHighsOnString - } // unwelcome_ipx_status + } // unwelcome_ipx_status } else { // PDLP has been used, so check whether claim of optimality // satisfies the HiGHS criteria diff --git a/src/test/DevKkt.cpp b/src/test/DevKkt.cpp index de393f7bf1..47941d9fd6 100644 --- a/src/test/DevKkt.cpp +++ b/src/test/DevKkt.cpp @@ -261,8 +261,8 @@ void checkComplementarySlackness(const State& state, if (fabs(state.colDual[i]) > tol && fabs(state.colValue[i] - state.colUpper[i]) > tol) { if (dev_print) - std::cout << "Comp. slackness fail: " - << "l[" << i << "]=" << state.colLower[i] << ", x[" << i + std::cout << "Comp. slackness fail: " << "l[" << i + << "]=" << state.colLower[i] << ", x[" << i << "]=" << state.colValue[i] << ", z[" << i << "]=" << state.colDual[i] << std::endl; infeas = fabs(state.colDual[i]); diff --git a/src/util/HighsMatrixUtils.cpp b/src/util/HighsMatrixUtils.cpp index 42f4375063..b55f4df79a 100644 --- a/src/util/HighsMatrixUtils.cpp +++ b/src/util/HighsMatrixUtils.cpp @@ -234,13 +234,18 @@ HighsStatus assessMatrix( error_found = true; assert(num_small_values == 0); } - highsLogUser(log_options, HighsLogType::kWarning, - "%s matrix packed vector contains %" HIGHSINT_FORMAT - " |values| in [%g, %g] " - "less than or equal to %g: ignored\n", - matrix_name.c_str(), num_small_values, min_small_value, - max_small_value, small_matrix_value); - warning_found = true; + // If explicit zeros are ignored, then no model information is + // lost, so only report and return a warning if small nonzeros are + // ignored + if (max_small_value > 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "%s matrix packed vector contains %" HIGHSINT_FORMAT + " |values| in [%g, %g] " + "less than or equal to %g: ignored\n", + matrix_name.c_str(), num_small_values, min_small_value, + max_small_value, small_matrix_value); + warning_found = true; + } } matrix_start[num_vec] = num_new_nz; HighsStatus return_status = HighsStatus::kOk;