From be4d4cb0fbd4ef2588756a32bb16dfab9d61f3c2 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Fri, 28 Feb 2025 12:40:15 +0000 Subject: [PATCH 1/6] Highs::readModel returns HighsStatus::kWarning if warning is issued in MPS read! --- check/TestFilereader.cpp | 20 ++++++----- src/io/Filereader.cpp | 1 + src/io/Filereader.h | 7 ++-- src/io/FilereaderMps.cpp | 10 ++++-- src/io/HMPSIO.cpp | 21 +++++++++--- src/io/HMPSIO.h | 1 + src/io/HMpsFF.cpp | 71 ++++++++++++++++++++++++++++++++-------- src/io/HMpsFF.h | 3 +- src/lp_data/Highs.cpp | 3 +- 9 files changed, 105 insertions(+), 32 deletions(-) diff --git a/check/TestFilereader.cpp b/check/TestFilereader.cpp index e08eced444..55f4baacd0 100644 --- a/check/TestFilereader.cpp +++ b/check/TestFilereader.cpp @@ -10,7 +10,7 @@ #include "lp_data/HighsLp.h" #include "lp_data/HighsLpUtils.h" -const bool dev_run = false; +const bool dev_run = true; const double inf = kHighsInf; TEST_CASE("filereader-edge-cases", "[highs_filereader]") { @@ -146,20 +146,16 @@ TEST_CASE("filereader-edge-cases", "[highs_filereader]") { void freeFixedModelTest(const std::string model_name) { std::string filename; filename = std::string(HIGHS_DIR) + "/check/instances/" + model_name + ".mps"; - HighsStatus status; Highs highs; highs.setOptionValue("output_flag", dev_run); - status = highs.readModel(filename); - REQUIRE(status == HighsStatus::kOk); + REQUIRE(highs.readModel(filename) == HighsStatus::kOk); HighsModel model_free = highs.getModel(); - status = highs.setOptionValue("mps_parser_type_free", false); - REQUIRE(status == HighsStatus::kOk); + REQUIRE(highs.setOptionValue("mps_parser_type_free", false) == HighsStatus::kOk); - status = highs.readModel(filename); - REQUIRE(status == HighsStatus::kOk); + REQUIRE(highs.readModel(filename) == HighsStatus::kWarning); HighsModel model_fixed = highs.getModel(); @@ -439,3 +435,11 @@ TEST_CASE("write-MI-bound-model", "[highs_filereader]") { REQUIRE(required_objective_value == h.getInfo().objective_function_value); std::remove(write_model_file.c_str()); } + +TEST_CASE("mps-warnings", "[highs_filereader]") { + std::string model_file = std::string(HIGHS_DIR) + "/check/instances/warnings.mps"; + Highs h; + //highs.setOptionValue("output_flag", dev_run); + HighsStatus return_status = h.readModel(model_file); + REQUIRE(return_status == HighsStatus::kWarning); +} diff --git a/src/io/Filereader.cpp b/src/io/Filereader.cpp index 5bb2c87028..a4caa576bc 100644 --- a/src/io/Filereader.cpp +++ b/src/io/Filereader.cpp @@ -75,6 +75,7 @@ void interpretFilereaderRetcode(const HighsLogOptions& log_options, const FilereaderRetcode code) { switch (code) { case FilereaderRetcode::kOk: + case FilereaderRetcode::kWarning: break; case FilereaderRetcode::kFileNotFound: highsLogUser(log_options, HighsLogType::kError, "File %s not found\n", diff --git a/src/io/Filereader.h b/src/io/Filereader.h index ea929eccf7..34481d7f10 100644 --- a/src/io/Filereader.h +++ b/src/io/Filereader.h @@ -17,9 +17,10 @@ enum class FilereaderRetcode { kOk = 0, - kFileNotFound = 1, - kParserError = 2, - kNotImplemented = 3, + kWarning = 1, + kFileNotFound = 2, + kParserError = 3, + kNotImplemented = 4, kTimeout }; diff --git a/src/io/FilereaderMps.cpp b/src/io/FilereaderMps.cpp index f13910a9f0..d135bf1ef8 100644 --- a/src/io/FilereaderMps.cpp +++ b/src/io/FilereaderMps.cpp @@ -28,7 +28,7 @@ FilereaderRetcode FilereaderMps::readModelFromFile(const HighsOptions& options, if (options.mps_parser_type_free) { HMpsFF parser{}; if (options.time_limit < kHighsInf && options.time_limit > 0) - parser.time_limit = options.time_limit; + parser.time_limit_ = options.time_limit; FreeFormatParserReturnCode result = parser.loadProblem(options.log_options, filename, model); @@ -36,7 +36,7 @@ FilereaderRetcode FilereaderMps::readModelFromFile(const HighsOptions& options, case FreeFormatParserReturnCode::kSuccess: lp.ensureColwise(); assert(model.lp_.objective_name_ != ""); - return FilereaderRetcode::kOk; + return parser.warning_issued_ ? FilereaderRetcode::kWarning : FilereaderRetcode::kOk; case FreeFormatParserReturnCode::kParserError: return FilereaderRetcode::kParserError; case FreeFormatParserReturnCode::kFileNotFound: @@ -55,6 +55,10 @@ FilereaderRetcode FilereaderMps::readModelFromFile(const HighsOptions& options, } // else use fixed format parser + // + // If the fixed format parser has had to be used, then a warning was + // issued, otherwise no warning has yet been issued + bool warning_issued = options.mps_parser_type_free; FilereaderRetcode return_code = readMps(options.log_options, filename, -1, -1, lp.num_row_, lp.num_col_, lp.sense_, lp.offset_, lp.a_matrix_.start_, lp.a_matrix_.index_, @@ -62,12 +66,14 @@ FilereaderRetcode FilereaderMps::readModelFromFile(const HighsOptions& options, lp.row_lower_, lp.row_upper_, lp.integrality_, lp.objective_name_, lp.col_names_, lp.row_names_, hessian.dim_, hessian.start_, hessian.index_, hessian.value_, lp.cost_row_location_, + warning_issued, options.keep_n_rows); if (return_code == FilereaderRetcode::kOk) lp.ensureColwise(); // Comment on existence of names with spaces hasNamesWithSpaces(options.log_options, lp.num_col_, lp.col_names_); hasNamesWithSpaces(options.log_options, lp.num_row_, lp.row_names_); assert(model.lp_.objective_name_ != ""); + if (return_code == FilereaderRetcode::kOk && warning_issued) return_code = FilereaderRetcode::kWarning; return return_code; } diff --git a/src/io/HMPSIO.cpp b/src/io/HMPSIO.cpp index ee38a36e31..d2aa572c9e 100644 --- a/src/io/HMPSIO.cpp +++ b/src/io/HMPSIO.cpp @@ -40,7 +40,11 @@ FilereaderRetcode readMps( vector& col_names, vector& row_names, HighsInt& Qdim, vector& Qstart, vector& Qindex, vector& Qvalue, HighsInt& cost_row_location, + bool& warning_issued, const HighsInt keep_n_rows) { + // Keep track of any warnings that are issued so that + // Highs::readModel can return HighsStatus::kWarning + warning_issued = false; // MPS file buffer numRow = 0; numCol = 0; @@ -205,12 +209,14 @@ FilereaderRetcode readMps( } Astart.push_back(Aindex.size()); - if (num_alien_entries) + if (num_alien_entries) { + warning_issued = true; highsLogUser(log_options, HighsLogType::kWarning, "COLUMNS section entries contain %8" HIGHSINT_FORMAT " with row not in ROWS " " section: ignored\n", num_alien_entries); + } highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read COLUMNS OK\n"); // Load RHS @@ -254,12 +260,14 @@ FilereaderRetcode readMps( } save_flag1 = flag[1]; } - if (num_alien_entries) + if (num_alien_entries) { + warning_issued = true; highsLogUser(log_options, HighsLogType::kWarning, "RHS section entries contain %8" HIGHSINT_FORMAT " with row not in ROWS " " section: ignored\n", num_alien_entries); + } highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read RHS OK\n"); // Load RANGES @@ -326,12 +334,14 @@ FilereaderRetcode readMps( break; } } - if (num_alien_entries) + if (num_alien_entries) { + warning_issued = true; highsLogUser(log_options, HighsLogType::kWarning, "RANGES section entries contain %8" HIGHSINT_FORMAT " with row not in ROWS " " section: ignored\n", num_alien_entries); + } highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read RANGES OK\n"); // Load BOUNDS @@ -383,6 +393,7 @@ FilereaderRetcode readMps( } // Load Hessian if (flag[0] == 'Q') { + warning_issued = true; highsLogUser( log_options, HighsLogType::kWarning, "Quadratic section: under development. Assumes QUADOBJ section\n"); @@ -443,12 +454,14 @@ FilereaderRetcode readMps( if (colUpper[iCol] >= kHighsInf) colUpper[iCol] = 1; } } - if (num_alien_entries) + if (num_alien_entries) { + warning_issued = true; highsLogUser(log_options, HighsLogType::kWarning, "BOUNDS section entries contain %8" HIGHSINT_FORMAT " with col not in " "COLUMNS section: ignored\n", num_alien_entries); + } highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read BOUNDS OK\n"); highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read ENDATA OK\n"); highsLogDev(log_options, HighsLogType::kInfo, diff --git a/src/io/HMPSIO.h b/src/io/HMPSIO.h index 021d9a3525..59be01b6b8 100644 --- a/src/io/HMPSIO.h +++ b/src/io/HMPSIO.h @@ -52,6 +52,7 @@ FilereaderRetcode readMps( vector& col_names, vector& row_names, HighsInt& Qdim, vector& Qstart, vector& Qindex, vector& Qvalue, HighsInt& cost_row_location, + bool& warning_issued, const HighsInt keep_n_rows = 0); HighsStatus writeMps( diff --git a/src/io/HMpsFF.cpp b/src/io/HMpsFF.cpp index 06ee0ea8f1..9c9e4b816f 100644 --- a/src/io/HMpsFF.cpp +++ b/src/io/HMpsFF.cpp @@ -21,6 +21,9 @@ const bool kNoClockCalls = false; FreeFormatParserReturnCode HMpsFF::loadProblem( const HighsLogOptions& log_options, const std::string filename, HighsModel& model) { + // Keep track of any warnings that are issued so that + // Highs::readModel can return HighsStatus::kWarning + warning_issued_ = false; HighsLp& lp = model.lp_; HighsHessian& hessian = model.hessian_; FreeFormatParserReturnCode result = parse(log_options, filename); @@ -58,6 +61,7 @@ FreeFormatParserReturnCode HMpsFF::loadProblem( // BOUNDS and other sections can only be defined for the first // occurrence if (has_duplicate_row_name_) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Linear constraints %d and %d have the same name \"%s\"\n", (int)duplicate_row_name_index0_, @@ -65,6 +69,7 @@ FreeFormatParserReturnCode HMpsFF::loadProblem( row_names.clear(); } if (has_duplicate_col_name_) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Variables %d and %d have the same name \"%s\"\n", (int)duplicate_col_name_index0_, @@ -210,7 +215,7 @@ HighsInt HMpsFF::fillHessian(const HighsLogOptions& log_options) { } bool HMpsFF::timeout() { - return time_limit > 0 && getWallTime() - start_time > time_limit; + return time_limit_ > 0 && getWallTime() - start_time > time_limit_; } bool HMpsFF::getMpsLine(std::istream& file, std::string& strline, bool& skip) { @@ -593,6 +598,7 @@ HMpsFF::Parsekey HMpsFF::parseRows(const HighsLogOptions& log_options, highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read ROWS OK\n"); if (!hasobj) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "No objective row found\n"); rowname2idx.emplace("artificial_empty_objective", -1); @@ -713,6 +719,12 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, }; bool skip; + size_t num_ignored_row_name = 0; + size_t report_ignored_row_name_frequency = 1; + size_t num_ignored_duplicate_cost_nz = 0; + size_t report_ignored_duplicate_cost_nz_frequency = 1; + size_t num_ignored_duplicate_matrix_nz = 0; + size_t report_ignored_duplicate_matrix_nz_frequency = 1; while (getMpsLine(file, strline, skip)) { if (skip) continue; if (timeout()) return HMpsFF::Parsekey::kTimeout; @@ -786,6 +798,7 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, name.c_str()); return HMpsFF::Parsekey::kFail; } else { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" with spaces has length %d, so assume " "fixed format\n", @@ -857,10 +870,14 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - highsLogUser( - log_options, HighsLogType::kWarning, - "Row name \"%s\" in COLUMNS section is not defined: ignored\n", - marker.c_str()); + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + warning_issued_ = true; + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in COLUMNS section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } } else { bool is_nan = false; double value = getValue(word, is_nan); // atof(word.c_str()); @@ -875,9 +892,14 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, if (col_value[rowidx]) { // Ignore duplicate entry num_nz--; - highsLogUser(log_options, HighsLogType::kWarning, - "Column \"%s\" has duplicate nonzero in row \"%s\"\n", - colname.c_str(), marker.c_str()); + num_ignored_duplicate_matrix_nz++; + if (num_ignored_duplicate_matrix_nz % report_ignored_duplicate_matrix_nz_frequency == 0) { + warning_issued_ = true; + highsLogUser(log_options, HighsLogType::kWarning, + "Column \"%s\" has duplicate nonzero in row \"%s\"\n", + colname.c_str(), marker.c_str()); + report_ignored_duplicate_matrix_nz_frequency *= 2; + } } else { col_value[rowidx] = value; col_index[col_count++] = rowidx; @@ -885,9 +907,14 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, } else if (rowidx == -1) { // Ignore duplicate entry if (col_cost) { - highsLogUser(log_options, HighsLogType::kWarning, - "Column \"%s\" has duplicate nonzero in row \"%s\"\n", - colname.c_str(), objective_name.c_str()); + num_ignored_duplicate_cost_nz++; + if (num_ignored_duplicate_cost_nz % report_ignored_duplicate_cost_nz_frequency == 0) { + warning_issued_ = true; + highsLogUser(log_options, HighsLogType::kWarning, + "Column \"%s\" has duplicate nonzero in objective row \"%s\"\n", + colname.c_str(), marker.c_str()); + report_ignored_duplicate_cost_nz_frequency *= 2; + } } else { col_cost = value; } @@ -917,6 +944,7 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { + warning_issued_ = true; highsLogUser( log_options, HighsLogType::kWarning, "Row name \"%s\" in COLUMNS section is not defined: ignored\n", @@ -936,6 +964,7 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, if (col_value[rowidx]) { // Ignore duplicate entry num_nz--; + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Column \"%s\" has duplicate nonzero in row \"%s\"\n", colname.c_str(), marker.c_str()); @@ -946,6 +975,7 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, } else if (rowidx == -1) { // Ignore duplicate entry if (col_cost) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Column \"%s\" has duplicate nonzero in row \"%s\"\n", colname.c_str(), objective_name.c_str()); @@ -1072,12 +1102,14 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, } if (mit == rowname2idx.end()) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" in RHS section is not defined: ignored\n", marker.c_str()); } else { parseName(marker, rowidx, has_entry); if (has_entry) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" in RHS section has duplicate definition: " "ignored\n", @@ -1116,6 +1148,7 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" in RHS section is not defined: ignored\n", marker.c_str()); @@ -1124,6 +1157,7 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, parseName(marker, rowidx, has_entry); if (has_entry) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" in RHS section has duplicate definition: " "ignored\n", @@ -1307,6 +1341,7 @@ HMpsFF::Parsekey HMpsFF::parseBounds(const HighsLogOptions& log_options, // Determine whether this entry yields a duplicate bound // definition if ((is_lb && has_lower[colidx]) || (is_ub && has_upper[colidx])) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Column name \"%s\" in BOUNDS section has duplicate " "definition: ignored\n", @@ -1369,10 +1404,12 @@ HMpsFF::Parsekey HMpsFF::parseBounds(const HighsLogOptions& log_options, // Must be LI, UI or SI, and value should be integer HighsInt i_value = static_cast(value); double dl = value - i_value; - if (dl) + if (dl) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Bound for LI/UI/SI column \"%s\" is %g: not integer\n", marker.c_str(), value); + } if (is_semi) { // Bound marker SI defines the column as semi-integer col_integrality[colidx] = HighsVarType::kSemiInteger; @@ -1462,6 +1499,7 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { + warning_issued_ = true; highsLogUser( log_options, HighsLogType::kWarning, "Row name \"%s\" in RANGES section is not defined: ignored\n", @@ -1469,11 +1507,13 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, } else { parseName(marker, rowidx); if (rowidx < 0) { + warning_issued_ = true; highsLogUser( log_options, HighsLogType::kWarning, "Row name \"%s\" in RANGES section is not valid: ignored\n", marker.c_str()); } else if (has_row_entry_[rowidx]) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" in RANGES section has duplicate " "definition: ignored\n", @@ -1508,6 +1548,7 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { + warning_issued_ = true; highsLogUser( log_options, HighsLogType::kWarning, "Row name \"%s\" in RANGES section is not defined: ignored\n", @@ -1515,11 +1556,13 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, } else { parseName(marker, rowidx); if (rowidx < 0) { + warning_issued_ = true; highsLogUser( log_options, HighsLogType::kWarning, "Row name \"%s\" in RANGES section is not valid: ignored\n", marker.c_str()); } else if (has_row_entry_[rowidx]) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" in RANGES section has duplicate " "definition: ignored\n", @@ -1680,10 +1723,12 @@ typename HMpsFF::Parsekey HMpsFF::parseQuadRows( auto mit = rowname2idx.find(rowname); // if row of section does not exist or is free (index -2), then skip if (mit == rowname2idx.end() || mit->second == -2) { - if (mit == rowname2idx.end()) + if (mit == rowname2idx.end()) { + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" in %s section is not defined: ignored\n", rowname.c_str(), section_name.c_str()); + } // read lines until start of new section bool skip; while (getMpsLine(file, strline, skip)) { diff --git a/src/io/HMpsFF.h b/src/io/HMpsFF.h index e527288e61..235f987408 100644 --- a/src/io/HMpsFF.h +++ b/src/io/HMpsFF.h @@ -60,7 +60,8 @@ class HMpsFF { const std::string filename, HighsModel& model); - double time_limit = kHighsInf; + double time_limit_ = kHighsInf; + bool warning_issued_ = false; private: double start_time; diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index 72c3a6166f..0e9468d8df 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -677,8 +677,9 @@ HighsStatus Highs::readModel(const std::string& filename) { if (call_code != FilereaderRetcode::kOk) { interpretFilereaderRetcode(options_.log_options, filename.c_str(), call_code); + const HighsStatus call_status = call_code == FilereaderRetcode::kWarning ? HighsStatus::kWarning : HighsStatus::kError; return_status = - interpretCallStatus(options_.log_options, HighsStatus::kError, + interpretCallStatus(options_.log_options, call_status, return_status, "readModelFromFile"); if (return_status == HighsStatus::kError) return return_status; } From 9b735ebb69d74eece6efb6b8bcf54e146859b5fc Mon Sep 17 00:00:00 2001 From: JAJHall Date: Fri, 28 Feb 2025 13:20:22 +0000 Subject: [PATCH 2/6] Now limiting reporting of warnings in COLUMNS section --- src/io/HMpsFF.cpp | 149 +++++++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 67 deletions(-) diff --git a/src/io/HMpsFF.cpp b/src/io/HMpsFF.cpp index 9c9e4b816f..14192dc47f 100644 --- a/src/io/HMpsFF.cpp +++ b/src/io/HMpsFF.cpp @@ -870,9 +870,9 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { + warning_issued_ = true; num_ignored_row_name++; if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { - warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" in COLUMNS section is not defined: ignored\n", marker.c_str()); @@ -892,12 +892,12 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, if (col_value[rowidx]) { // Ignore duplicate entry num_nz--; + warning_issued_ = true; num_ignored_duplicate_matrix_nz++; if (num_ignored_duplicate_matrix_nz % report_ignored_duplicate_matrix_nz_frequency == 0) { - warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, - "Column \"%s\" has duplicate nonzero in row \"%s\"\n", - colname.c_str(), marker.c_str()); + "Column \"%s\" has duplicate nonzero %g in row \"%s\": ignored\n", + colname.c_str(), value, marker.c_str()); report_ignored_duplicate_matrix_nz_frequency *= 2; } } else { @@ -907,12 +907,12 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, } else if (rowidx == -1) { // Ignore duplicate entry if (col_cost) { + warning_issued_ = true; num_ignored_duplicate_cost_nz++; if (num_ignored_duplicate_cost_nz % report_ignored_duplicate_cost_nz_frequency == 0) { - warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, - "Column \"%s\" has duplicate nonzero in objective row \"%s\"\n", - colname.c_str(), marker.c_str()); + "Column \"%s\" has duplicate nonzero %g in objective row \"%s\": ignored\n", + colname.c_str(), value, marker.c_str()); report_ignored_duplicate_cost_nz_frequency *= 2; } } else { @@ -944,11 +944,14 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - warning_issued_ = true; - highsLogUser( - log_options, HighsLogType::kWarning, - "Row name \"%s\" in COLUMNS section is not defined: ignored\n", - marker.c_str()); + warning_issued_ = true; + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in COLUMNS section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } continue; }; bool is_nan = false; @@ -964,10 +967,14 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, if (col_value[rowidx]) { // Ignore duplicate entry num_nz--; - warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Column \"%s\" has duplicate nonzero in row \"%s\"\n", - colname.c_str(), marker.c_str()); + warning_issued_ = true; + num_ignored_duplicate_matrix_nz++; + if (num_ignored_duplicate_matrix_nz % report_ignored_duplicate_matrix_nz_frequency == 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Column \"%s\" has duplicate nonzero %g in row \"%s\": ignored\n", + colname.c_str(), value, marker.c_str()); + report_ignored_duplicate_matrix_nz_frequency *= 2; + } } else { col_value[rowidx] = value; col_index[col_count++] = rowidx; @@ -975,10 +982,14 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, } else if (rowidx == -1) { // Ignore duplicate entry if (col_cost) { - warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Column \"%s\" has duplicate nonzero in row \"%s\"\n", - colname.c_str(), objective_name.c_str()); + warning_issued_ = true; + num_ignored_duplicate_cost_nz++; + if (num_ignored_duplicate_cost_nz % report_ignored_duplicate_cost_nz_frequency == 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Column \"%s\" has duplicate nonzero %g in objective row \"%s\": ignored\n", + colname.c_str(), value, objective_name.c_str()); + report_ignored_duplicate_cost_nz_frequency *= 2; + } } else { col_cost = value; } @@ -1102,21 +1113,21 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, } if (mit == rowname2idx.end()) { - warning_issued_ = true; + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" in RHS section is not defined: ignored\n", marker.c_str()); } else { + bool is_nan = false; + double value = getValue(word, is_nan); // atof(word.c_str()); parseName(marker, rowidx, has_entry); if (has_entry) { - warning_issued_ = true; + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RHS section has duplicate definition: " + "Row name \"%s\" in RHS section has duplicate value %g: " "ignored\n", - marker.c_str()); + marker.c_str(), value); } else { - bool is_nan = false; - double value = getValue(word, is_nan); // atof(word.c_str()); if (is_nan) { highsLogUser(log_options, HighsLogType::kError, "RHS for row \"%s\" is NaN\n", marker.c_str()); @@ -1148,7 +1159,7 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - warning_issued_ = true; + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" in RHS section is not defined: ignored\n", marker.c_str()); @@ -1156,15 +1167,15 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, }; parseName(marker, rowidx, has_entry); + bool is_nan = false; + double value = getValue(word, is_nan); // atof(word.c_str()); if (has_entry) { - warning_issued_ = true; + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RHS section has duplicate definition: " + "Row name \"%s\" in RHS section has duplicate value %g: " "ignored\n", - marker.c_str()); + marker.c_str(), value); } else { - bool is_nan = false; - double value = getValue(word, is_nan); // atof(word.c_str()); if (is_nan) { highsLogUser(log_options, HighsLogType::kError, "RHS for row \"%s\" is NaN\n", marker.c_str()); @@ -1341,11 +1352,11 @@ HMpsFF::Parsekey HMpsFF::parseBounds(const HighsLogOptions& log_options, // Determine whether this entry yields a duplicate bound // definition if ((is_lb && has_lower[colidx]) || (is_ub && has_upper[colidx])) { - warning_issued_ = true; + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, - "Column name \"%s\" in BOUNDS section has duplicate " + "Column name \"%s\" in BOUNDS section has duplicate %s bound " "definition: ignored\n", - marker.c_str()); + marker.c_str(), is_lb ? "lower" : "upper"); continue; } @@ -1499,7 +1510,7 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - warning_issued_ = true; + warning_issued_ = true; highsLogUser( log_options, HighsLogType::kWarning, "Row name \"%s\" in RANGES section is not defined: ignored\n", @@ -1507,26 +1518,28 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, } else { parseName(marker, rowidx); if (rowidx < 0) { - warning_issued_ = true; + warning_issued_ = true; highsLogUser( log_options, HighsLogType::kWarning, "Row name \"%s\" in RANGES section is not valid: ignored\n", marker.c_str()); - } else if (has_row_entry_[rowidx]) { - warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section has duplicate " - "definition: ignored\n", - marker.c_str()); } else { bool is_nan = false; double value = getValue(word, is_nan); // atof(word.c_str()); - if (is_nan) { - highsLogUser(log_options, HighsLogType::kError, - "Range for row \"%s\" is NaN\n", marker.c_str()); - return HMpsFF::Parsekey::kFail; - } - addRhs(value, rowidx); + if (has_row_entry_[rowidx]) { + warning_issued_ = true; + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section has duplicate " + "value %g: ignored\n", + marker.c_str(), value); + } else { + if (is_nan) { + highsLogUser(log_options, HighsLogType::kError, + "Range for row \"%s\" is NaN\n", marker.c_str()); + return HMpsFF::Parsekey::kFail; + } + addRhs(value, rowidx); + } } } @@ -1548,7 +1561,7 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - warning_issued_ = true; + warning_issued_ = true; highsLogUser( log_options, HighsLogType::kWarning, "Row name \"%s\" in RANGES section is not defined: ignored\n", @@ -1556,27 +1569,29 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, } else { parseName(marker, rowidx); if (rowidx < 0) { - warning_issued_ = true; + warning_issued_ = true; highsLogUser( log_options, HighsLogType::kWarning, "Row name \"%s\" in RANGES section is not valid: ignored\n", marker.c_str()); - } else if (has_row_entry_[rowidx]) { - warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section has duplicate " - "definition: ignored\n", - marker.c_str()); } else { - bool is_nan = false; - double value = getValue(word, is_nan); // atof(word.c_str()); - if (is_nan) { - highsLogUser(log_options, HighsLogType::kError, - "Range for row \"%s\" is NaN\n", marker.c_str()); - return HMpsFF::Parsekey::kFail; - } - addRhs(value, rowidx); - } + bool is_nan = false; + double value = getValue(word, is_nan); // atof(word.c_str()); + if (has_row_entry_[rowidx]) { + warning_issued_ = true; + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section has duplicate " + "value %g: ignored\n", + marker.c_str(), value); + } else { + if (is_nan) { + highsLogUser(log_options, HighsLogType::kError, + "Range for row \"%s\" is NaN\n", marker.c_str()); + return HMpsFF::Parsekey::kFail; + } + addRhs(value, rowidx); + } + } } if (!is_end(strline, end)) { @@ -1724,7 +1739,7 @@ typename HMpsFF::Parsekey HMpsFF::parseQuadRows( // if row of section does not exist or is free (index -2), then skip if (mit == rowname2idx.end() || mit->second == -2) { if (mit == rowname2idx.end()) { - warning_issued_ = true; + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" in %s section is not defined: ignored\n", rowname.c_str(), section_name.c_str()); From 430e092c7309ea51b8548dd6d5d61b5fc7e915f4 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Fri, 28 Feb 2025 13:59:44 +0000 Subject: [PATCH 3/6] Now limiting reporting of warnings in RHS section --- src/io/HMpsFF.cpp | 48 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/io/HMpsFF.cpp b/src/io/HMpsFF.cpp index 14192dc47f..3d6c13e113 100644 --- a/src/io/HMpsFF.cpp +++ b/src/io/HMpsFF.cpp @@ -1049,6 +1049,10 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, bool has_entry = false; bool skip; + size_t num_ignored_row_name = 0; + size_t report_ignored_row_name_frequency = 1; + size_t num_ignored_duplicate_rhs = 0; + size_t report_ignored_duplicate_rhs_frequency = 1; while (getMpsLine(file, strline, skip)) { if (skip) continue; if (timeout()) return HMpsFF::Parsekey::kTimeout; @@ -1114,19 +1118,27 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, if (mit == rowname2idx.end()) { warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RHS section is not defined: ignored\n", - marker.c_str()); + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RHS section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } } else { bool is_nan = false; double value = getValue(word, is_nan); // atof(word.c_str()); parseName(marker, rowidx, has_entry); if (has_entry) { warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RHS section has duplicate value %g: " - "ignored\n", - marker.c_str(), value); + num_ignored_duplicate_rhs++; + if (num_ignored_duplicate_rhs % report_ignored_duplicate_rhs_frequency == 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RHS section has duplicate value %g: " + "ignored\n", + marker.c_str(), value); + report_ignored_duplicate_rhs_frequency *= 2; + } } else { if (is_nan) { highsLogUser(log_options, HighsLogType::kError, @@ -1160,9 +1172,13 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RHS section is not defined: ignored\n", - marker.c_str()); + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RHS section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } continue; }; @@ -1171,10 +1187,14 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, double value = getValue(word, is_nan); // atof(word.c_str()); if (has_entry) { warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RHS section has duplicate value %g: " - "ignored\n", - marker.c_str(), value); + num_ignored_duplicate_rhs++; + if (num_ignored_duplicate_rhs % report_ignored_duplicate_rhs_frequency == 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RHS section has duplicate value %g: " + "ignored\n", + marker.c_str(), value); + report_ignored_duplicate_rhs_frequency *= 2; + } } else { if (is_nan) { highsLogUser(log_options, HighsLogType::kError, From eb211508aea0ab3b365aefcc659cd48313c43fa0 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Fri, 28 Feb 2025 15:03:20 +0000 Subject: [PATCH 4/6] Now limiting reporting of warnings in RANGES section --- src/io/HMpsFF.cpp | 140 ++++++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 49 deletions(-) diff --git a/src/io/HMpsFF.cpp b/src/io/HMpsFF.cpp index 3d6c13e113..942afac4a6 100644 --- a/src/io/HMpsFF.cpp +++ b/src/io/HMpsFF.cpp @@ -748,6 +748,11 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, col_count = 0; } + warning_issued_ = num_ignored_row_name > 0 || num_ignored_duplicate_cost_nz > 0 || num_ignored_duplicate_matrix_nz > 0; + if (warning_issued_) + highsLogUser(log_options, HighsLogType::kWarning, + "COLUMNS section: ignored %d undefined rows %d duplicate cost values and %d duplicate matrix values\n", + int(num_ignored_row_name), int(num_ignored_duplicate_cost_nz), int(num_ignored_duplicate_matrix_nz)); highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read COLUMNS OK\n"); return key; @@ -870,7 +875,6 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - warning_issued_ = true; num_ignored_row_name++; if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { highsLogUser(log_options, HighsLogType::kWarning, @@ -892,7 +896,6 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, if (col_value[rowidx]) { // Ignore duplicate entry num_nz--; - warning_issued_ = true; num_ignored_duplicate_matrix_nz++; if (num_ignored_duplicate_matrix_nz % report_ignored_duplicate_matrix_nz_frequency == 0) { highsLogUser(log_options, HighsLogType::kWarning, @@ -907,7 +910,6 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, } else if (rowidx == -1) { // Ignore duplicate entry if (col_cost) { - warning_issued_ = true; num_ignored_duplicate_cost_nz++; if (num_ignored_duplicate_cost_nz % report_ignored_duplicate_cost_nz_frequency == 0) { highsLogUser(log_options, HighsLogType::kWarning, @@ -944,7 +946,6 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - warning_issued_ = true; num_ignored_row_name++; if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { highsLogUser(log_options, HighsLogType::kWarning, @@ -967,7 +968,6 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, if (col_value[rowidx]) { // Ignore duplicate entry num_nz--; - warning_issued_ = true; num_ignored_duplicate_matrix_nz++; if (num_ignored_duplicate_matrix_nz % report_ignored_duplicate_matrix_nz_frequency == 0) { highsLogUser(log_options, HighsLogType::kWarning, @@ -982,7 +982,6 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, } else if (rowidx == -1) { // Ignore duplicate entry if (col_cost) { - warning_issued_ = true; num_ignored_duplicate_cost_nz++; if (num_ignored_duplicate_cost_nz % report_ignored_duplicate_cost_nz_frequency == 0) { highsLogUser(log_options, HighsLogType::kWarning, @@ -1064,6 +1063,11 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, // start of new section? if (key != Parsekey::kNone && key != Parsekey::kRhs) { + warning_issued_ = num_ignored_row_name > 0 || num_ignored_duplicate_rhs > 0; + if (warning_issued_) + highsLogUser(log_options, HighsLogType::kWarning, + "RHS section: ignored %d undefined rows and %d duplicate values\n", + int(num_ignored_row_name), int(num_ignored_duplicate_rhs)); highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read RHS OK\n"); return key; @@ -1117,7 +1121,6 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, } if (mit == rowname2idx.end()) { - warning_issued_ = true; num_ignored_row_name++; if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { highsLogUser(log_options, HighsLogType::kWarning, @@ -1130,7 +1133,6 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, double value = getValue(word, is_nan); // atof(word.c_str()); parseName(marker, rowidx, has_entry); if (has_entry) { - warning_issued_ = true; num_ignored_duplicate_rhs++; if (num_ignored_duplicate_rhs % report_ignored_duplicate_rhs_frequency == 0) { highsLogUser(log_options, HighsLogType::kWarning, @@ -1171,7 +1173,6 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - warning_issued_ = true; num_ignored_row_name++; if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { highsLogUser(log_options, HighsLogType::kWarning, @@ -1186,7 +1187,6 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, bool is_nan = false; double value = getValue(word, is_nan); // atof(word.c_str()); if (has_entry) { - warning_issued_ = true; num_ignored_duplicate_rhs++; if (num_ignored_duplicate_rhs % report_ignored_duplicate_rhs_frequency == 0) { highsLogUser(log_options, HighsLogType::kWarning, @@ -1227,6 +1227,10 @@ HMpsFF::Parsekey HMpsFF::parseBounds(const HighsLogOptions& log_options, has_upper.assign(num_col, false); bool skip; + size_t num_ignored_duplicate_bound = 0; + size_t report_ignored_duplicate_bound_frequency = 1; + size_t num_fractional_integer_bound = 0; + size_t report_fractional_integer_bound_frequency = 1; while (getMpsLine(file, strline, skip)) { if (skip) continue; if (timeout()) return HMpsFF::Parsekey::kTimeout; @@ -1273,6 +1277,11 @@ HMpsFF::Parsekey HMpsFF::parseBounds(const HighsLogOptions& log_options, log_options, HighsLogType::kInfo, "Number of SC entries in BOUNDS section is %" HIGHSINT_FORMAT "\n", num_sc); + warning_issued_ = num_ignored_duplicate_bound || num_fractional_integer_bound > 0; + if (warning_issued_) + highsLogUser(log_options, HighsLogType::kWarning, + "BOUNDS section: ignored %d duplicate values and %d fractional integer bounds\n", + int(num_ignored_duplicate_bound), int(num_fractional_integer_bound)); highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read BOUNDS OK\n"); return key; @@ -1372,11 +1381,14 @@ HMpsFF::Parsekey HMpsFF::parseBounds(const HighsLogOptions& log_options, // Determine whether this entry yields a duplicate bound // definition if ((is_lb && has_lower[colidx]) || (is_ub && has_upper[colidx])) { - warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Column name \"%s\" in BOUNDS section has duplicate %s bound " - "definition: ignored\n", - marker.c_str(), is_lb ? "lower" : "upper"); + num_ignored_duplicate_bound++; + if (num_ignored_duplicate_bound % report_ignored_duplicate_bound_frequency == 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Column name \"%s\" in BOUNDS section has duplicate %s bound " + "definition: ignored\n", + marker.c_str(), is_lb ? "lower" : "upper"); + report_ignored_duplicate_bound_frequency *= 2; + } continue; } @@ -1436,10 +1448,13 @@ HMpsFF::Parsekey HMpsFF::parseBounds(const HighsLogOptions& log_options, HighsInt i_value = static_cast(value); double dl = value - i_value; if (dl) { - warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Bound for LI/UI/SI column \"%s\" is %g: not integer\n", - marker.c_str(), value); + num_fractional_integer_bound++; + if (num_fractional_integer_bound % report_fractional_integer_bound_frequency == 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Bound for LI/UI/SI column \"%s\" is %g: not integer\n", + marker.c_str(), value); + report_fractional_integer_bound_frequency *= 2; + } } if (is_semi) { // Bound marker SI defines the column as semi-integer @@ -1497,6 +1512,10 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, has_row_entry_.assign(num_row, false); bool skip; + size_t num_ignored_row_name = 0; + size_t report_ignored_row_name_frequency = 1; + size_t num_ignored_duplicate_range = 0; + size_t report_ignored_duplicate_range_frequency = 1; while (getMpsLine(file, strline, skip)) { if (skip) continue; if (timeout()) return HMpsFF::Parsekey::kTimeout; @@ -1506,6 +1525,11 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, HMpsFF::Parsekey key = checkFirstWord(strline, begin, end, word); if (key != Parsekey::kNone) { + warning_issued_ = num_ignored_row_name > 0 || num_ignored_duplicate_range > 0; + if (warning_issued_) + highsLogUser(log_options, HighsLogType::kWarning, + "RANGES section: ignored %d undefined/illegal rows and %d duplicate values\n", + int(num_ignored_row_name), int(num_ignored_duplicate_range)); highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read RANGES OK\n"); return key; @@ -1530,28 +1554,37 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - warning_issued_ = true; - highsLogUser( - log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section is not defined: ignored\n", - marker.c_str()); + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser( + log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } } else { parseName(marker, rowidx); if (rowidx < 0) { - warning_issued_ = true; - highsLogUser( - log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section is not valid: ignored\n", - marker.c_str()); + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser( + log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section is not valid: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } } else { bool is_nan = false; double value = getValue(word, is_nan); // atof(word.c_str()); if (has_row_entry_[rowidx]) { - warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section has duplicate " - "value %g: ignored\n", - marker.c_str(), value); + num_ignored_duplicate_range++; + if (num_ignored_duplicate_range % report_ignored_duplicate_range_frequency == 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section has duplicate " + "value %g: ignored\n", + marker.c_str(), value); + report_ignored_duplicate_range_frequency *= 2; + } } else { if (is_nan) { highsLogUser(log_options, HighsLogType::kError, @@ -1581,28 +1614,37 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - warning_issued_ = true; - highsLogUser( - log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section is not defined: ignored\n", - marker.c_str()); + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser( + log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } } else { parseName(marker, rowidx); if (rowidx < 0) { - warning_issued_ = true; - highsLogUser( - log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section is not valid: ignored\n", - marker.c_str()); + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser( + log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section is not valid: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } } else { bool is_nan = false; double value = getValue(word, is_nan); // atof(word.c_str()); if (has_row_entry_[rowidx]) { - warning_issued_ = true; - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section has duplicate " - "value %g: ignored\n", - marker.c_str(), value); + num_ignored_duplicate_range++; + if (num_ignored_duplicate_range % report_ignored_duplicate_range_frequency == 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section has duplicate " + "value %g: ignored\n", + marker.c_str(), value); + report_ignored_duplicate_range_frequency *= 2; + } } else { if (is_nan) { highsLogUser(log_options, HighsLogType::kError, From 6cc8acbb1c636ab639614ac4b12437d96a57e34e Mon Sep 17 00:00:00 2001 From: JAJHall Date: Fri, 28 Feb 2025 15:08:48 +0000 Subject: [PATCH 5/6] Added warnings.mps, and formatted --- check/TestFilereader.cpp | 10 +- check/instances/warnings.mps | 68 +++++++ src/io/FilereaderMps.cpp | 9 +- src/io/HMPSIO.cpp | 3 +- src/io/HMPSIO.h | 3 +- src/io/HMpsFF.cpp | 375 +++++++++++++++++++---------------- src/lp_data/Highs.cpp | 9 +- 7 files changed, 294 insertions(+), 183 deletions(-) create mode 100644 check/instances/warnings.mps diff --git a/check/TestFilereader.cpp b/check/TestFilereader.cpp index 55f4baacd0..2d1e5223f6 100644 --- a/check/TestFilereader.cpp +++ b/check/TestFilereader.cpp @@ -10,7 +10,7 @@ #include "lp_data/HighsLp.h" #include "lp_data/HighsLpUtils.h" -const bool dev_run = true; +const bool dev_run = false; const double inf = kHighsInf; TEST_CASE("filereader-edge-cases", "[highs_filereader]") { @@ -153,7 +153,8 @@ void freeFixedModelTest(const std::string model_name) { HighsModel model_free = highs.getModel(); - REQUIRE(highs.setOptionValue("mps_parser_type_free", false) == HighsStatus::kOk); + REQUIRE(highs.setOptionValue("mps_parser_type_free", false) == + HighsStatus::kOk); REQUIRE(highs.readModel(filename) == HighsStatus::kWarning); @@ -437,9 +438,10 @@ TEST_CASE("write-MI-bound-model", "[highs_filereader]") { } TEST_CASE("mps-warnings", "[highs_filereader]") { - std::string model_file = std::string(HIGHS_DIR) + "/check/instances/warnings.mps"; + std::string model_file = + std::string(HIGHS_DIR) + "/check/instances/warnings.mps"; Highs h; - //highs.setOptionValue("output_flag", dev_run); + // highs.setOptionValue("output_flag", dev_run); HighsStatus return_status = h.readModel(model_file); REQUIRE(return_status == HighsStatus::kWarning); } diff --git a/check/instances/warnings.mps b/check/instances/warnings.mps new file mode 100644 index 0000000000..02376e1414 --- /dev/null +++ b/check/instances/warnings.mps @@ -0,0 +1,68 @@ +NAME WARNINGS +ROWS + G R0 + G R1 + G R2 + G R3 + G R4 + G R5 + N COST +COLUMNS + C0 R0 -1.0 COST -1.0 + C0 R11 1.0 R12 1.0 + C0 R13 1.0 R14 1.0 + C0 R15 1.0 COST 1.0 + C0 COST 2.0 COST 3.0 + C0 COST 4.0 COST 5.0 + C0 R0 1.0 R0 2.0 + C0 R0 3.0 R0 4.0 + C0 R0 5.0 R0 6.0 + MARK0000 'MARKER' 'INTORG' + C1 R0 -1.0 COST -1.0 + C2 R0 -1.0 COST -1.0 + C3 R0 -1.0 COST -1.0 + C4 R0 -1.0 COST -1.0 + C5 R0 -1.0 COST -1.0 + MARK0001 'MARKER' 'INTEND' + C6 R0 -1.0 COST -1.0 + C7 R0 -1.0 COST -1.0 + C8 R0 -1.0 COST -1.0 +RHS + DEMANDS R0 -1.0 R11 -1.0 + DEMANDS R12 -1.0 R13 -1.0 + DEMANDS R14 -1.0 R15 -1.0 + DEMANDS R0 1.0 R0 2.0 + DEMANDS R0 3.0 R0 4.0 + DEMANDS R0 5.0 +BOUNDS + UP SERVINGS C0 -1.0 + UP SERVINGS C0 1.0 + UP SERVINGS C0 2.0 + UP SERVINGS C0 3.0 + UP SERVINGS C0 4.0 + UP SERVINGS C0 5.0 + UP SERVINGS C10 1.0 + LI SERVINGS C1 1.1 + UI SERVINGS C2 2.1 + SI SERVINGS C3 3.1 + LI SERVINGS C4 4.1 + UI SERVINGS C5 5.1 + LO SERVINGS C4 1.0 + LO SERVINGS C6 1.0 + LO SERVINGS C7 1.0 + LO SERVINGS C8 1.0 + UP SERVINGS C4 -1.0 + UP SERVINGS C6 -1.0 + UP SERVINGS C7 -1.0 + UP SERVINGS C8 -1.0 +RANGES + RNG R0 0.1 R0 1.0 + RNG R0 2.0 R0 3.0 + RNG R0 4.0 R0 5.0 + RNG R11 0.1 R12 1.0 + RNG R13 2.0 R14 3.0 + RNG R15 4.0 COST 5.0 + RNG COST 6.0 COST 7.0 + RNG COST 8.0 COST 9.0 + +ENDATA diff --git a/src/io/FilereaderMps.cpp b/src/io/FilereaderMps.cpp index d135bf1ef8..8a93a9c775 100644 --- a/src/io/FilereaderMps.cpp +++ b/src/io/FilereaderMps.cpp @@ -36,7 +36,8 @@ FilereaderRetcode FilereaderMps::readModelFromFile(const HighsOptions& options, case FreeFormatParserReturnCode::kSuccess: lp.ensureColwise(); assert(model.lp_.objective_name_ != ""); - return parser.warning_issued_ ? FilereaderRetcode::kWarning : FilereaderRetcode::kOk; + return parser.warning_issued_ ? FilereaderRetcode::kWarning + : FilereaderRetcode::kOk; case FreeFormatParserReturnCode::kParserError: return FilereaderRetcode::kParserError; case FreeFormatParserReturnCode::kFileNotFound: @@ -66,14 +67,14 @@ FilereaderRetcode FilereaderMps::readModelFromFile(const HighsOptions& options, lp.row_lower_, lp.row_upper_, lp.integrality_, lp.objective_name_, lp.col_names_, lp.row_names_, hessian.dim_, hessian.start_, hessian.index_, hessian.value_, lp.cost_row_location_, - warning_issued, - options.keep_n_rows); + warning_issued, options.keep_n_rows); if (return_code == FilereaderRetcode::kOk) lp.ensureColwise(); // Comment on existence of names with spaces hasNamesWithSpaces(options.log_options, lp.num_col_, lp.col_names_); hasNamesWithSpaces(options.log_options, lp.num_row_, lp.row_names_); assert(model.lp_.objective_name_ != ""); - if (return_code == FilereaderRetcode::kOk && warning_issued) return_code = FilereaderRetcode::kWarning; + if (return_code == FilereaderRetcode::kOk && warning_issued) + return_code = FilereaderRetcode::kWarning; return return_code; } diff --git a/src/io/HMPSIO.cpp b/src/io/HMPSIO.cpp index d2aa572c9e..661356dc0c 100644 --- a/src/io/HMPSIO.cpp +++ b/src/io/HMPSIO.cpp @@ -39,8 +39,7 @@ FilereaderRetcode readMps( vector& integerColumn, std::string& objective_name, vector& col_names, vector& row_names, HighsInt& Qdim, vector& Qstart, vector& Qindex, - vector& Qvalue, HighsInt& cost_row_location, - bool& warning_issued, + vector& Qvalue, HighsInt& cost_row_location, bool& warning_issued, const HighsInt keep_n_rows) { // Keep track of any warnings that are issued so that // Highs::readModel can return HighsStatus::kWarning diff --git a/src/io/HMPSIO.h b/src/io/HMPSIO.h index 59be01b6b8..74decb7868 100644 --- a/src/io/HMPSIO.h +++ b/src/io/HMPSIO.h @@ -51,8 +51,7 @@ FilereaderRetcode readMps( vector& integerColumn, std::string& objective_name, vector& col_names, vector& row_names, HighsInt& Qdim, vector& Qstart, vector& Qindex, - vector& Qvalue, HighsInt& cost_row_location, - bool& warning_issued, + vector& Qvalue, HighsInt& cost_row_location, bool& warning_issued, const HighsInt keep_n_rows = 0); HighsStatus writeMps( diff --git a/src/io/HMpsFF.cpp b/src/io/HMpsFF.cpp index 942afac4a6..65dc0f8adf 100644 --- a/src/io/HMpsFF.cpp +++ b/src/io/HMpsFF.cpp @@ -598,7 +598,7 @@ HMpsFF::Parsekey HMpsFF::parseRows(const HighsLogOptions& log_options, highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read ROWS OK\n"); if (!hasobj) { - warning_issued_ = true; + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "No objective row found\n"); rowname2idx.emplace("artificial_empty_objective", -1); @@ -748,11 +748,16 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, col_count = 0; } - warning_issued_ = num_ignored_row_name > 0 || num_ignored_duplicate_cost_nz > 0 || num_ignored_duplicate_matrix_nz > 0; - if (warning_issued_) - highsLogUser(log_options, HighsLogType::kWarning, - "COLUMNS section: ignored %d undefined rows %d duplicate cost values and %d duplicate matrix values\n", - int(num_ignored_row_name), int(num_ignored_duplicate_cost_nz), int(num_ignored_duplicate_matrix_nz)); + warning_issued_ = num_ignored_row_name > 0 || + num_ignored_duplicate_cost_nz > 0 || + num_ignored_duplicate_matrix_nz > 0; + if (warning_issued_) + highsLogUser(log_options, HighsLogType::kWarning, + "COLUMNS section: ignored %d undefined rows %d duplicate " + "cost values and %d duplicate matrix values\n", + int(num_ignored_row_name), + int(num_ignored_duplicate_cost_nz), + int(num_ignored_duplicate_matrix_nz)); highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read COLUMNS OK\n"); return key; @@ -803,7 +808,7 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, name.c_str()); return HMpsFF::Parsekey::kFail; } else { - warning_issued_ = true; + warning_issued_ = true; highsLogUser(log_options, HighsLogType::kWarning, "Row name \"%s\" with spaces has length %d, so assume " "fixed format\n", @@ -877,10 +882,11 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, if (mit == rowname2idx.end()) { num_ignored_row_name++; if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in COLUMNS section is not defined: ignored\n", - marker.c_str()); - report_ignored_row_name_frequency *= 2; + highsLogUser( + log_options, HighsLogType::kWarning, + "Row name \"%s\" in COLUMNS section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; } } else { bool is_nan = false; @@ -896,13 +902,16 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, if (col_value[rowidx]) { // Ignore duplicate entry num_nz--; - num_ignored_duplicate_matrix_nz++; - if (num_ignored_duplicate_matrix_nz % report_ignored_duplicate_matrix_nz_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Column \"%s\" has duplicate nonzero %g in row \"%s\": ignored\n", - colname.c_str(), value, marker.c_str()); - report_ignored_duplicate_matrix_nz_frequency *= 2; - } + num_ignored_duplicate_matrix_nz++; + if (num_ignored_duplicate_matrix_nz % + report_ignored_duplicate_matrix_nz_frequency == + 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Column \"%s\" has duplicate nonzero %g in row " + "\"%s\": ignored\n", + colname.c_str(), value, marker.c_str()); + report_ignored_duplicate_matrix_nz_frequency *= 2; + } } else { col_value[rowidx] = value; col_index[col_count++] = rowidx; @@ -910,13 +919,16 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, } else if (rowidx == -1) { // Ignore duplicate entry if (col_cost) { - num_ignored_duplicate_cost_nz++; - if (num_ignored_duplicate_cost_nz % report_ignored_duplicate_cost_nz_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Column \"%s\" has duplicate nonzero %g in objective row \"%s\": ignored\n", - colname.c_str(), value, marker.c_str()); - report_ignored_duplicate_cost_nz_frequency *= 2; - } + num_ignored_duplicate_cost_nz++; + if (num_ignored_duplicate_cost_nz % + report_ignored_duplicate_cost_nz_frequency == + 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Column \"%s\" has duplicate nonzero %g in " + "objective row \"%s\": ignored\n", + colname.c_str(), value, marker.c_str()); + report_ignored_duplicate_cost_nz_frequency *= 2; + } } else { col_cost = value; } @@ -946,13 +958,14 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - num_ignored_row_name++; - if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in COLUMNS section is not defined: ignored\n", - marker.c_str()); - report_ignored_row_name_frequency *= 2; - } + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser( + log_options, HighsLogType::kWarning, + "Row name \"%s\" in COLUMNS section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } continue; }; bool is_nan = false; @@ -968,13 +981,16 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, if (col_value[rowidx]) { // Ignore duplicate entry num_nz--; - num_ignored_duplicate_matrix_nz++; - if (num_ignored_duplicate_matrix_nz % report_ignored_duplicate_matrix_nz_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Column \"%s\" has duplicate nonzero %g in row \"%s\": ignored\n", - colname.c_str(), value, marker.c_str()); - report_ignored_duplicate_matrix_nz_frequency *= 2; - } + num_ignored_duplicate_matrix_nz++; + if (num_ignored_duplicate_matrix_nz % + report_ignored_duplicate_matrix_nz_frequency == + 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Column \"%s\" has duplicate nonzero %g in row " + "\"%s\": ignored\n", + colname.c_str(), value, marker.c_str()); + report_ignored_duplicate_matrix_nz_frequency *= 2; + } } else { col_value[rowidx] = value; col_index[col_count++] = rowidx; @@ -982,13 +998,16 @@ typename HMpsFF::Parsekey HMpsFF::parseCols(const HighsLogOptions& log_options, } else if (rowidx == -1) { // Ignore duplicate entry if (col_cost) { - num_ignored_duplicate_cost_nz++; - if (num_ignored_duplicate_cost_nz % report_ignored_duplicate_cost_nz_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Column \"%s\" has duplicate nonzero %g in objective row \"%s\": ignored\n", - colname.c_str(), value, objective_name.c_str()); - report_ignored_duplicate_cost_nz_frequency *= 2; - } + num_ignored_duplicate_cost_nz++; + if (num_ignored_duplicate_cost_nz % + report_ignored_duplicate_cost_nz_frequency == + 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Column \"%s\" has duplicate nonzero %g in " + "objective row \"%s\": ignored\n", + colname.c_str(), value, objective_name.c_str()); + report_ignored_duplicate_cost_nz_frequency *= 2; + } } else { col_cost = value; } @@ -1063,11 +1082,13 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, // start of new section? if (key != Parsekey::kNone && key != Parsekey::kRhs) { - warning_issued_ = num_ignored_row_name > 0 || num_ignored_duplicate_rhs > 0; - if (warning_issued_) - highsLogUser(log_options, HighsLogType::kWarning, - "RHS section: ignored %d undefined rows and %d duplicate values\n", - int(num_ignored_row_name), int(num_ignored_duplicate_rhs)); + warning_issued_ = + num_ignored_row_name > 0 || num_ignored_duplicate_rhs > 0; + if (warning_issued_) + highsLogUser( + log_options, HighsLogType::kWarning, + "RHS section: ignored %d undefined rows and %d duplicate values\n", + int(num_ignored_row_name), int(num_ignored_duplicate_rhs)); highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read RHS OK\n"); return key; @@ -1123,24 +1144,26 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, if (mit == rowname2idx.end()) { num_ignored_row_name++; if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RHS section is not defined: ignored\n", - marker.c_str()); - report_ignored_row_name_frequency *= 2; + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RHS section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; } } else { bool is_nan = false; double value = getValue(word, is_nan); // atof(word.c_str()); parseName(marker, rowidx, has_entry); if (has_entry) { - num_ignored_duplicate_rhs++; - if (num_ignored_duplicate_rhs % report_ignored_duplicate_rhs_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RHS section has duplicate value %g: " - "ignored\n", - marker.c_str(), value); - report_ignored_duplicate_rhs_frequency *= 2; - } + num_ignored_duplicate_rhs++; + if (num_ignored_duplicate_rhs % + report_ignored_duplicate_rhs_frequency == + 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RHS section has duplicate value %g: " + "ignored\n", + marker.c_str(), value); + report_ignored_duplicate_rhs_frequency *= 2; + } } else { if (is_nan) { highsLogUser(log_options, HighsLogType::kError, @@ -1173,13 +1196,14 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - num_ignored_row_name++; - if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RHS section is not defined: ignored\n", - marker.c_str()); - report_ignored_row_name_frequency *= 2; - } + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser( + log_options, HighsLogType::kWarning, + "Row name \"%s\" in RHS section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } continue; }; @@ -1187,14 +1211,16 @@ HMpsFF::Parsekey HMpsFF::parseRhs(const HighsLogOptions& log_options, bool is_nan = false; double value = getValue(word, is_nan); // atof(word.c_str()); if (has_entry) { - num_ignored_duplicate_rhs++; - if (num_ignored_duplicate_rhs % report_ignored_duplicate_rhs_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RHS section has duplicate value %g: " - "ignored\n", - marker.c_str(), value); - report_ignored_duplicate_rhs_frequency *= 2; - } + num_ignored_duplicate_rhs++; + if (num_ignored_duplicate_rhs % + report_ignored_duplicate_rhs_frequency == + 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RHS section has duplicate value %g: " + "ignored\n", + marker.c_str(), value); + report_ignored_duplicate_rhs_frequency *= 2; + } } else { if (is_nan) { highsLogUser(log_options, HighsLogType::kError, @@ -1277,11 +1303,14 @@ HMpsFF::Parsekey HMpsFF::parseBounds(const HighsLogOptions& log_options, log_options, HighsLogType::kInfo, "Number of SC entries in BOUNDS section is %" HIGHSINT_FORMAT "\n", num_sc); - warning_issued_ = num_ignored_duplicate_bound || num_fractional_integer_bound > 0; - if (warning_issued_) - highsLogUser(log_options, HighsLogType::kWarning, - "BOUNDS section: ignored %d duplicate values and %d fractional integer bounds\n", - int(num_ignored_duplicate_bound), int(num_fractional_integer_bound)); + warning_issued_ = + num_ignored_duplicate_bound || num_fractional_integer_bound > 0; + if (warning_issued_) + highsLogUser(log_options, HighsLogType::kWarning, + "BOUNDS section: ignored %d duplicate values and %d " + "fractional integer bounds\n", + int(num_ignored_duplicate_bound), + int(num_fractional_integer_bound)); highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read BOUNDS OK\n"); return key; @@ -1382,12 +1411,15 @@ HMpsFF::Parsekey HMpsFF::parseBounds(const HighsLogOptions& log_options, // definition if ((is_lb && has_lower[colidx]) || (is_ub && has_upper[colidx])) { num_ignored_duplicate_bound++; - if (num_ignored_duplicate_bound % report_ignored_duplicate_bound_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Column name \"%s\" in BOUNDS section has duplicate %s bound " - "definition: ignored\n", - marker.c_str(), is_lb ? "lower" : "upper"); - report_ignored_duplicate_bound_frequency *= 2; + if (num_ignored_duplicate_bound % + report_ignored_duplicate_bound_frequency == + 0) { + highsLogUser( + log_options, HighsLogType::kWarning, + "Column name \"%s\" in BOUNDS section has duplicate %s bound " + "definition: ignored\n", + marker.c_str(), is_lb ? "lower" : "upper"); + report_ignored_duplicate_bound_frequency *= 2; } continue; } @@ -1448,13 +1480,15 @@ HMpsFF::Parsekey HMpsFF::parseBounds(const HighsLogOptions& log_options, HighsInt i_value = static_cast(value); double dl = value - i_value; if (dl) { - num_fractional_integer_bound++; - if (num_fractional_integer_bound % report_fractional_integer_bound_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Bound for LI/UI/SI column \"%s\" is %g: not integer\n", - marker.c_str(), value); - report_fractional_integer_bound_frequency *= 2; - } + num_fractional_integer_bound++; + if (num_fractional_integer_bound % + report_fractional_integer_bound_frequency == + 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Bound for LI/UI/SI column \"%s\" is %g: not integer\n", + marker.c_str(), value); + report_fractional_integer_bound_frequency *= 2; + } } if (is_semi) { // Bound marker SI defines the column as semi-integer @@ -1525,11 +1559,14 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, HMpsFF::Parsekey key = checkFirstWord(strline, begin, end, word); if (key != Parsekey::kNone) { - warning_issued_ = num_ignored_row_name > 0 || num_ignored_duplicate_range > 0; - if (warning_issued_) - highsLogUser(log_options, HighsLogType::kWarning, - "RANGES section: ignored %d undefined/illegal rows and %d duplicate values\n", - int(num_ignored_row_name), int(num_ignored_duplicate_range)); + warning_issued_ = + num_ignored_row_name > 0 || num_ignored_duplicate_range > 0; + if (warning_issued_) + highsLogUser(log_options, HighsLogType::kWarning, + "RANGES section: ignored %d undefined/illegal rows and %d " + "duplicate values\n", + int(num_ignored_row_name), + int(num_ignored_duplicate_range)); highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read RANGES OK\n"); return key; @@ -1556,43 +1593,45 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, if (mit == rowname2idx.end()) { num_ignored_row_name++; if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { - highsLogUser( - log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section is not defined: ignored\n", - marker.c_str()); - report_ignored_row_name_frequency *= 2; + highsLogUser( + log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; } } else { parseName(marker, rowidx); if (rowidx < 0) { - num_ignored_row_name++; - if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { - highsLogUser( - log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section is not valid: ignored\n", - marker.c_str()); - report_ignored_row_name_frequency *= 2; - } + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser( + log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section is not valid: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } } else { bool is_nan = false; double value = getValue(word, is_nan); // atof(word.c_str()); - if (has_row_entry_[rowidx]) { - num_ignored_duplicate_range++; - if (num_ignored_duplicate_range % report_ignored_duplicate_range_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section has duplicate " - "value %g: ignored\n", - marker.c_str(), value); - report_ignored_duplicate_range_frequency *= 2; - } - } else { - if (is_nan) { - highsLogUser(log_options, HighsLogType::kError, - "Range for row \"%s\" is NaN\n", marker.c_str()); - return HMpsFF::Parsekey::kFail; - } - addRhs(value, rowidx); - } + if (has_row_entry_[rowidx]) { + num_ignored_duplicate_range++; + if (num_ignored_duplicate_range % + report_ignored_duplicate_range_frequency == + 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section has duplicate " + "value %g: ignored\n", + marker.c_str(), value); + report_ignored_duplicate_range_frequency *= 2; + } + } else { + if (is_nan) { + highsLogUser(log_options, HighsLogType::kError, + "Range for row \"%s\" is NaN\n", marker.c_str()); + return HMpsFF::Parsekey::kFail; + } + addRhs(value, rowidx); + } } } @@ -1614,46 +1653,48 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options, auto mit = rowname2idx.find(marker); if (mit == rowname2idx.end()) { - num_ignored_row_name++; - if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { - highsLogUser( - log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section is not defined: ignored\n", - marker.c_str()); - report_ignored_row_name_frequency *= 2; - } + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser( + log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section is not defined: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } } else { parseName(marker, rowidx); if (rowidx < 0) { - num_ignored_row_name++; - if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { - highsLogUser( - log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section is not valid: ignored\n", - marker.c_str()); - report_ignored_row_name_frequency *= 2; - } + num_ignored_row_name++; + if (num_ignored_row_name % report_ignored_row_name_frequency == 0) { + highsLogUser( + log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section is not valid: ignored\n", + marker.c_str()); + report_ignored_row_name_frequency *= 2; + } } else { - bool is_nan = false; - double value = getValue(word, is_nan); // atof(word.c_str()); - if (has_row_entry_[rowidx]) { - num_ignored_duplicate_range++; - if (num_ignored_duplicate_range % report_ignored_duplicate_range_frequency == 0) { - highsLogUser(log_options, HighsLogType::kWarning, - "Row name \"%s\" in RANGES section has duplicate " - "value %g: ignored\n", - marker.c_str(), value); - report_ignored_duplicate_range_frequency *= 2; - } - } else { - if (is_nan) { - highsLogUser(log_options, HighsLogType::kError, - "Range for row \"%s\" is NaN\n", marker.c_str()); - return HMpsFF::Parsekey::kFail; - } - addRhs(value, rowidx); - } - } + bool is_nan = false; + double value = getValue(word, is_nan); // atof(word.c_str()); + if (has_row_entry_[rowidx]) { + num_ignored_duplicate_range++; + if (num_ignored_duplicate_range % + report_ignored_duplicate_range_frequency == + 0) { + highsLogUser(log_options, HighsLogType::kWarning, + "Row name \"%s\" in RANGES section has duplicate " + "value %g: ignored\n", + marker.c_str(), value); + report_ignored_duplicate_range_frequency *= 2; + } + } else { + if (is_nan) { + highsLogUser(log_options, HighsLogType::kError, + "Range for row \"%s\" is NaN\n", marker.c_str()); + return HMpsFF::Parsekey::kFail; + } + addRhs(value, rowidx); + } + } } if (!is_end(strline, end)) { diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index 0e9468d8df..e449e8205a 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -677,10 +677,11 @@ HighsStatus Highs::readModel(const std::string& filename) { if (call_code != FilereaderRetcode::kOk) { interpretFilereaderRetcode(options_.log_options, filename.c_str(), call_code); - const HighsStatus call_status = call_code == FilereaderRetcode::kWarning ? HighsStatus::kWarning : HighsStatus::kError; - return_status = - interpretCallStatus(options_.log_options, call_status, - return_status, "readModelFromFile"); + const HighsStatus call_status = call_code == FilereaderRetcode::kWarning + ? HighsStatus::kWarning + : HighsStatus::kError; + return_status = interpretCallStatus(options_.log_options, call_status, + return_status, "readModelFromFile"); if (return_status == HighsStatus::kError) return return_status; } model.lp_.model_name_ = extractModelName(filename); From 9acbd2ad8a1ee3201397b14774062a59888deb37 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Fri, 28 Feb 2025 17:51:00 +0000 Subject: [PATCH 6/6] Allowed MPS section names to be used as names for columns, RHS, ranges, bounds etc by only accepting them as section names if nothing follows on the line --- check/TestFilereader.cpp | 11 +++++- check/TestMipSolver.cpp | 1 + check/instances/silly-names.mps | 14 ++++++++ src/io/HMpsFF.cpp | 62 ++++++++++++++++++++------------- 4 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 check/instances/silly-names.mps diff --git a/check/TestFilereader.cpp b/check/TestFilereader.cpp index 2d1e5223f6..dbe52fc1c8 100644 --- a/check/TestFilereader.cpp +++ b/check/TestFilereader.cpp @@ -441,7 +441,16 @@ TEST_CASE("mps-warnings", "[highs_filereader]") { std::string model_file = std::string(HIGHS_DIR) + "/check/instances/warnings.mps"; Highs h; - // highs.setOptionValue("output_flag", dev_run); + h.setOptionValue("output_flag", dev_run); HighsStatus return_status = h.readModel(model_file); REQUIRE(return_status == HighsStatus::kWarning); } + +TEST_CASE("mps-silly-names", "[highs_filereader]") { + std::string model_file = + std::string(HIGHS_DIR) + "/check/instances/silly-names.mps"; + Highs h; + h.setOptionValue("output_flag", dev_run); + HighsStatus return_status = h.readModel(model_file); + REQUIRE(return_status == HighsStatus::kOk); +} diff --git a/check/TestMipSolver.cpp b/check/TestMipSolver.cpp index c4ea7f584a..f4aeccb557 100644 --- a/check/TestMipSolver.cpp +++ b/check/TestMipSolver.cpp @@ -812,6 +812,7 @@ TEST_CASE("issue-2122", "[highs_test_mip_solver]") { TEST_CASE("issue-2171", "[highs_test_mip_solver]") { std::string filename = std::string(HIGHS_DIR) + "/check/instances/2171.mps"; Highs highs; + highs.setOptionValue("output_flag", dev_run); highs.setOptionValue("mip_rel_gap", 0); highs.setOptionValue("mip_abs_gap", 0); highs.readModel(filename); diff --git a/check/instances/silly-names.mps b/check/instances/silly-names.mps new file mode 100644 index 0000000000..21f7599def --- /dev/null +++ b/check/instances/silly-names.mps @@ -0,0 +1,14 @@ +NAME SILLY-NAMES +OBJSENSE MIN +ROWS + G R0 + N COST +COLUMNS + C0 R0 -1.0 COST -1.0 + OBJSENSE R0 -1.0 COST -1.0 + RANGES R0 -1.0 COST -1.0 + RHS R0 -1.0 COST -1.0 + C1 R0 -1.0 COST -1.0 +RHS + DEMANDS R0 -1.0 +ENDATA diff --git a/src/io/HMpsFF.cpp b/src/io/HMpsFF.cpp index 65dc0f8adf..17fee8d8dd 100644 --- a/src/io/HMpsFF.cpp +++ b/src/io/HMpsFF.cpp @@ -414,56 +414,70 @@ HMpsFF::Parsekey HMpsFF::checkFirstWord(std::string& strline, size_t& start, upper_word == "CSECTION") section_args = strline.substr(end, strline.length()); + HMpsFF::Parsekey key; + if (upper_word == "NAME") - return HMpsFF::Parsekey::kName; + key = HMpsFF::Parsekey::kName; else if (upper_word == "OBJSENSE") - return HMpsFF::Parsekey::kObjsense; + key = HMpsFF::Parsekey::kObjsense; else if (upper_word == "MAX") - return HMpsFF::Parsekey::kMax; + key = HMpsFF::Parsekey::kMax; else if (upper_word == "MIN") - return HMpsFF::Parsekey::kMin; + key = HMpsFF::Parsekey::kMin; else if (upper_word == "ROWS") - return HMpsFF::Parsekey::kRows; + key = HMpsFF::Parsekey::kRows; else if (upper_word == "COLUMNS") - return HMpsFF::Parsekey::kCols; + key = HMpsFF::Parsekey::kCols; else if (upper_word == "RHS") - return HMpsFF::Parsekey::kRhs; + key = HMpsFF::Parsekey::kRhs; else if (upper_word == "BOUNDS") - return HMpsFF::Parsekey::kBounds; + key = HMpsFF::Parsekey::kBounds; else if (upper_word == "RANGES") - return HMpsFF::Parsekey::kRanges; + key = HMpsFF::Parsekey::kRanges; else if (upper_word == "QSECTION") - return HMpsFF::Parsekey::kQsection; + key = HMpsFF::Parsekey::kQsection; else if (upper_word == "QMATRIX") - return HMpsFF::Parsekey::kQmatrix; + key = HMpsFF::Parsekey::kQmatrix; else if (upper_word == "QUADOBJ") - return HMpsFF::Parsekey::kQuadobj; + key = HMpsFF::Parsekey::kQuadobj; else if (upper_word == "QCMATRIX") - return HMpsFF::Parsekey::kQcmatrix; + key = HMpsFF::Parsekey::kQcmatrix; else if (upper_word == "CSECTION") - return HMpsFF::Parsekey::kCsection; + key = HMpsFF::Parsekey::kCsection; else if (upper_word == "DELAYEDROWS") - return HMpsFF::Parsekey::kDelayedrows; + key = HMpsFF::Parsekey::kDelayedrows; else if (upper_word == "MODELCUTS") - return HMpsFF::Parsekey::kModelcuts; + key = HMpsFF::Parsekey::kModelcuts; else if (upper_word == "INDICATORS") - return HMpsFF::Parsekey::kIndicators; + key = HMpsFF::Parsekey::kIndicators; else if (upper_word == "SETS") - return HMpsFF::Parsekey::kSets; + key = HMpsFF::Parsekey::kSets; else if (upper_word == "SOS") - return HMpsFF::Parsekey::kSos; + key = HMpsFF::Parsekey::kSos; else if (upper_word == "GENCONS") - return HMpsFF::Parsekey::kGencons; + key = HMpsFF::Parsekey::kGencons; else if (upper_word == "PWLOBJ") - return HMpsFF::Parsekey::kPwlobj; + key = HMpsFF::Parsekey::kPwlobj; else if (upper_word == "PWLNAM") - return HMpsFF::Parsekey::kPwlnam; + key = HMpsFF::Parsekey::kPwlnam; else if (upper_word == "PWLCON") - return HMpsFF::Parsekey::kPwlcon; + key = HMpsFF::Parsekey::kPwlcon; else if (upper_word == "ENDATA") - return HMpsFF::Parsekey::kEnd; + key = HMpsFF::Parsekey::kEnd; else return HMpsFF::Parsekey::kNone; + // Can have keywords used as column names or names of RHS, BOUND, + // RANGES etc, so assume this if there are non-blanks after the + // apparent keyword. Only cases that don't work are NAME, OBJSENSE, + // QCMATRIX and QSECTION, since they can be followed by text + if (key == HMpsFF::Parsekey::kName || key == HMpsFF::Parsekey::kObjsense || + key == HMpsFF::Parsekey::kQcmatrix || key == HMpsFF::Parsekey::kQsection) + return key; + assert(key != HMpsFF::Parsekey::kNone); + + if (is_end(strline, end)) return key; + + return HMpsFF::Parsekey::kNone; } HighsInt HMpsFF::getColIdx(const std::string& colname, const bool add_if_new) {