Skip to content

Commit 3a95f64

Browse files
committed
Being more consistent about returning HighsStatus::kWarning when writing a model, solution or basis; formatted
1 parent 4bedda6 commit 3a95f64

File tree

10 files changed

+70
-113
lines changed

10 files changed

+70
-113
lines changed

check/TestBasis.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,16 @@ TEST_CASE("Basis-file", "[highs_basis_file]") {
5656
f << "HiGHS v1" << std::endl;
5757
f << "None" << std::endl;
5858
f.close();
59-
return_status = highs.readBasis(invalid_basis_file);
60-
REQUIRE(return_status == HighsStatus::kOk);
59+
// HiGHS v1 basis file is deprecated, but read, so warning is
60+
// returned
61+
REQUIRE(highs.readBasis(invalid_basis_file) == HighsStatus::kWarning);
62+
63+
// Write and read a file for an invalid basis
64+
f.open(invalid_basis_file, std::ios::out);
65+
f << "HiGHS_basis_file v2" << std::endl;
66+
f << "None" << std::endl;
67+
f.close();
68+
REQUIRE(highs.readBasis(invalid_basis_file) == HighsStatus::kOk);
6169

6270
// Write and read a file for incompatible number of columns
6371
f.open(invalid_basis_file, std::ios::out);

check/TestCheckSolution.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ TEST_CASE("check-set-mip-solution", "[highs_check_solution]") {
9090
if (dev_run) printf("Num nodes = %d\n", int(scratch_num_nodes));
9191

9292
std::string solution_file = test_name + model + ".sol";
93-
if (dev_run) return_status = highs.writeSolution("");
94-
return_status = highs.writeSolution(solution_file);
95-
REQUIRE(return_status == HighsStatus::kOk);
93+
if (dev_run) REQUIRE(highs.writeSolution("") == HighsStatus::kOk);
94+
;
95+
REQUIRE(highs.writeSolution(solution_file) == HighsStatus::kOk);
9696

9797
highs.clear();
9898

@@ -590,9 +590,15 @@ void runWriteReadCheckSolution(Highs& highs, const std::string& test_name,
590590
if (dev_run)
591591
printf("Writing solution in style %d to %s\n", int(write_solution_style),
592592
solution_file.c_str());
593-
if (dev_run) return_status = highs.writeSolution("", write_solution_style);
594-
return_status = highs.writeSolution(solution_file, write_solution_style);
595-
REQUIRE(return_status == HighsStatus::kOk);
593+
// For models without names, Highs::writeSolution will return
594+
// HighsStatus::kWarning
595+
HighsStatus require_status = highs.getLp().col_names_.size()
596+
? HighsStatus::kOk
597+
: HighsStatus::kWarning;
598+
REQUIRE(highs.writeSolution(solution_file, write_solution_style) ==
599+
require_status);
600+
if (dev_run)
601+
REQUIRE(highs.writeSolution("", write_solution_style) == HighsStatus::kOk);
596602

597603
const bool& value_valid = highs.getSolution().value_valid;
598604
bool valid, integral, feasible;

check/TestFilereader.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ TEST_CASE("handle-blank-space-names", "[highs_filereader]") {
491491
lp.a_matrix_.index_ = {0, 1, 0, 1};
492492
lp.a_matrix_.value_ = {1, 2, 2, 4};
493493
Highs h;
494-
// h.setOptionValue("output_flag", dev_run);
494+
h.setOptionValue("output_flag", dev_run);
495495
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
496496
h.run();
497497
// Names will be created when writing the solution
@@ -513,7 +513,6 @@ TEST_CASE("handle-blank-space-names", "[highs_filereader]") {
513513
std::vector<double> value = {2, 3};
514514
REQUIRE(h.addRow(5, inf, 2, index.data(), value.data()) == HighsStatus::kOk);
515515
h.run();
516-
printf("h.getLP().row_names_[2] = %s\n", h.getLp().row_names_[2].c_str());
517516
REQUIRE(h.writeBasis("") == HighsStatus::kWarning);
518517
REQUIRE(h.writeSolution("", kSolutionStylePretty) == HighsStatus::kOk);
519518
REQUIRE(h.writeModel("") == HighsStatus::kOk);

check/TestNames.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ TEST_CASE("highs-names", "[highs_names]") {
111111
local_lp.col_names_.clear();
112112
local_lp.row_names_.clear();
113113
highs.passModel(local_lp);
114-
REQUIRE(highs.writeSolution(solution_file, 1) == HighsStatus::kOk);
114+
REQUIRE(highs.writeSolution(solution_file, 1) == HighsStatus::kWarning);
115115

116116
std::remove(solution_file.c_str());
117117
}

check/TestQpSolver.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,8 @@ TEST_CASE("rowless-qp", "[qpsolver]") {
11211121
REQUIRE(highs.setOptionValue("qp_regularization_value", 0) ==
11221122
HighsStatus::kOk);
11231123
REQUIRE(highs.run() == HighsStatus::kOk);
1124-
REQUIRE(highs.writeSolution("", kSolutionStylePretty) == HighsStatus::kOk);
1124+
REQUIRE(highs.writeSolution("", kSolutionStylePretty) ==
1125+
HighsStatus::kWarning);
11251126

11261127
const double required_objective_function_value = -2.25;
11271128

highs/lp_data/Highs.cpp

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -755,9 +755,8 @@ HighsStatus Highs::writeLocalModel(HighsModel& model,
755755
// Replace any blank names and return error if there are duplicates
756756
// or names with spaces
757757
call_status = normaliseNames(this->options_.log_options, lp);
758-
return_status =
759-
interpretCallStatus(options_.log_options, call_status,
760-
return_status, "normaliseNames");
758+
return_status = interpretCallStatus(options_.log_options, call_status,
759+
return_status, "normaliseNames");
761760
if (return_status == HighsStatus::kError) return return_status;
762761

763762
// Ensure that the LP is column-wise
@@ -827,9 +826,8 @@ HighsStatus Highs::writeBasis(const std::string& filename) {
827826
// Replace any blank names and return error if there are duplicates
828827
// or names with spaces
829828
call_status = normaliseNames(this->options_.log_options, this->model_.lp_);
830-
return_status =
831-
interpretCallStatus(options_.log_options, call_status,
832-
return_status, "normaliseNames");
829+
return_status = interpretCallStatus(options_.log_options, call_status,
830+
return_status, "normaliseNames");
833831
if (return_status == HighsStatus::kError) return return_status;
834832

835833
// Report to user that basis is being written
@@ -3369,9 +3367,8 @@ HighsStatus Highs::writeSolution(const std::string& filename,
33693367
// Replace any blank names and return error if there are duplicates
33703368
// or names with spaces
33713369
call_status = normaliseNames(this->options_.log_options, this->model_.lp_);
3372-
return_status =
3373-
interpretCallStatus(options_.log_options, call_status,
3374-
return_status, "normaliseNames");
3370+
return_status = interpretCallStatus(options_.log_options, call_status,
3371+
return_status, "normaliseNames");
33753372
if (return_status == HighsStatus::kError) return return_status;
33763373

33773374
// Report to user that solution is being written

highs/lp_data/HighsInterface.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ HighsStatus Highs::addColsInterface(
513513
// Update the basis corresponding to new nonbasic columns
514514
if (useful_basis) appendNonbasicColsToBasisInterface(ext_num_new_col);
515515

516-
// Possibly add column names
516+
// Possibly add blank column names
517517
lp.addColNames("", ext_num_new_col);
518518

519519
// Increase the number of columns in the LP
@@ -658,7 +658,7 @@ HighsStatus Highs::addRowsInterface(HighsInt ext_num_new_row,
658658
// Update the basis corresponding to new basic rows
659659
if (useful_basis) appendBasicRowsToBasisInterface(ext_num_new_row);
660660

661-
// Possibly add row names
661+
// Possibly add blank row names
662662
lp.addRowNames("", ext_num_new_row);
663663

664664
// Increase the number of rows in the LP

highs/lp_data/HighsLp.cpp

Lines changed: 9 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -345,95 +345,26 @@ void HighsLp::userCostScale(const HighsInt user_cost_scale) {
345345
}
346346

347347
void HighsLp::addColNames(const std::string name, const HighsInt num_new_col) {
348-
// Don't add names if there are no columns, or if the names are
349-
// already incomplete
348+
// Don't add names if there are no columns being added
350349
if (this->num_col_ == 0) return;
351350
HighsInt col_names_size = this->col_names_.size();
352-
if (col_names_size < this->num_col_) return;
353-
if (!this->col_hash_.name2index.size())
354-
this->col_hash_.form(this->col_names_);
351+
if (col_names_size <= 0) return;
352+
assert(col_names_size == this->num_col_ + num_new_col);
355353
// Handle the addition of user-defined names later
356354
assert(name == "");
357-
if (this->col_name_prefix_ == "")
358-
col_name_prefix_ = kHighsUniqueColNamePrefix;
359-
for (HighsInt iCol = this->num_col_; iCol < this->num_col_ + num_new_col;
360-
iCol++) {
361-
const std::string col_name =
362-
this->col_name_prefix_ + std::to_string(this->col_name_suffix_++);
363-
bool added = false;
364-
auto search = this->col_hash_.name2index.find(col_name);
365-
if (search == this->col_hash_.name2index.end()) {
366-
// Name not found in hash
367-
if (col_names_size == this->num_col_) {
368-
// No space (or name) for this col name
369-
this->col_names_.push_back(col_name);
370-
added = true;
371-
} else if (col_names_size > iCol) {
372-
// Space for this col name. Only add if name is blank
373-
if (this->col_names_[iCol] == "") {
374-
this->col_names_[iCol] = col_name;
375-
added = true;
376-
}
377-
}
378-
}
379-
if (added) {
380-
const bool duplicate =
381-
!this->col_hash_.name2index.emplace(col_name, iCol).second;
382-
assert(!duplicate);
383-
assert(this->col_names_[iCol] == col_name);
384-
assert(this->col_hash_.name2index.find(col_name)->second == iCol);
385-
} else {
386-
// Duplicate name or other failure
387-
this->col_hash_.name2index.clear();
388-
return;
389-
}
390-
}
355+
// Blank names for the new columns were added in
356+
// appendColsToLpVectors
391357
}
392358

393359
void HighsLp::addRowNames(const std::string name, const HighsInt num_new_row) {
394-
// Don't add names if there are no rows, or if the names are already
395-
// incomplete
360+
// Don't add names if there are no rows being added
396361
if (this->num_row_ == 0) return;
397362
HighsInt row_names_size = this->row_names_.size();
398-
if (row_names_size < this->num_row_) return;
399-
if (!this->row_hash_.name2index.size())
400-
this->row_hash_.form(this->row_names_);
363+
if (row_names_size <= 0) return;
364+
assert(row_names_size == this->num_row_ + num_new_row);
401365
// Handle the addition of user-defined names later
402366
assert(name == "");
403-
if (this->row_name_prefix_ == "")
404-
row_name_prefix_ = kHighsUniquerowNamePrefix;
405-
for (HighsInt iRow = this->num_row_; iRow < this->num_row_ + num_new_row;
406-
iRow++) {
407-
const std::string row_name =
408-
this->row_name_prefix_ + std::to_string(this->row_name_suffix_++);
409-
bool added = false;
410-
auto search = this->row_hash_.name2index.find(row_name);
411-
if (search == this->row_hash_.name2index.end()) {
412-
// Name not found in hash
413-
if (row_names_size == this->num_row_) {
414-
// No space (or name) for this row name
415-
this->row_names_.push_back(row_name);
416-
added = true;
417-
} else if (row_names_size > iRow) {
418-
// Space for this row name. Only add if name is blank
419-
if (this->row_names_[iRow] == "") {
420-
this->row_names_[iRow] = row_name;
421-
added = true;
422-
}
423-
}
424-
}
425-
if (added) {
426-
const bool duplicate =
427-
!this->row_hash_.name2index.emplace(row_name, iRow).second;
428-
assert(!duplicate);
429-
assert(this->row_names_[iRow] == row_name);
430-
assert(this->row_hash_.name2index.find(row_name)->second == iRow);
431-
} else {
432-
// Duplicate name or other failure
433-
this->row_hash_.name2index.clear();
434-
return;
435-
}
436-
}
367+
// Blank names for the new rows were added in appendRowsToLpVectors
437368
}
438369

439370
void HighsLp::deleteColsFromVectors(

highs/lp_data/HighsLpUtils.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,7 +1864,8 @@ void reportLpColVectors(const HighsLogOptions& log_options, const HighsLp& lp) {
18641864
std::string type;
18651865
HighsInt count;
18661866
bool have_integer_columns = (getNumInt(lp) != 0);
1867-
bool have_col_names = lp.col_names_.size() == static_cast<size_t>(lp.num_col_);
1867+
bool have_col_names =
1868+
lp.col_names_.size() == static_cast<size_t>(lp.num_col_);
18681869

18691870
highsLogUser(log_options, HighsLogType::kInfo,
18701871
" Column Lower Upper Cost "
@@ -2515,7 +2516,8 @@ HighsStatus assessLpPrimalSolution(const std::string message,
25152516
row_value.assign(lp.num_row_, 0);
25162517
const bool have_integrality = (lp.integrality_.size() != 0);
25172518
if (!solution.value_valid) return HighsStatus::kError;
2518-
const bool have_col_names = lp.col_names_.size() == static_cast<size_t>(lp.num_col_);
2519+
const bool have_col_names =
2520+
lp.col_names_.size() == static_cast<size_t>(lp.num_col_);
25192521
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
25202522
const std::string name_string =
25212523
have_col_names ? (" (" + lp.col_names_[iCol] + ")") : "";
@@ -3108,8 +3110,10 @@ HighsLp withoutSemiVariables(const HighsLp& lp_, HighsSolution& solution,
31083110
HighsInt semi_row_num = 0;
31093111
// Insert the new variables and their coefficients
31103112
std::stringstream ss;
3111-
const bool have_col_names = lp.col_names_.size() == static_cast<size_t>(lp.num_col_);
3112-
const bool have_row_names = lp.row_names_.size() == static_cast<size_t>(lp.num_row_);
3113+
const bool have_col_names =
3114+
lp.col_names_.size() == static_cast<size_t>(lp.num_col_);
3115+
const bool have_row_names =
3116+
lp.row_names_.size() == static_cast<size_t>(lp.num_row_);
31133117
const bool have_solution = solution.value_valid;
31143118
if (have_solution) {
31153119
// Create zeroed row values for the new rows

highs/lp_data/HighsModelUtils.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,13 @@ void writePrimalSolution(FILE* file, const HighsLogOptions& log_options,
205205
const HighsLp& lp,
206206
const std::vector<double>& primal_solution,
207207
const bool sparse) {
208+
// Use when writing out the solution file (when names can be assumed
209+
// to exist) and the improving solution in the MIP solver (when
210+
// names cannot be assumed to exist)
208211
HighsInt num_nonzero_primal_value = 0;
209-
assert(lp.col_names_.size() == static_cast<size_t>(lp.num_col_));
212+
const bool have_col_names = lp.col_names_.size() > 0;
213+
if (have_col_names)
214+
assert(lp.col_names_.size() == static_cast<size_t>(lp.num_col_));
210215
if (sparse) {
211216
// Determine the number of nonzero primal solution values
212217
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
@@ -225,8 +230,12 @@ void writePrimalSolution(FILE* file, const HighsLogOptions& log_options,
225230
if (sparse && !primal_solution[ix]) continue;
226231
auto valStr = highsDoubleToString(primal_solution[ix],
227232
kHighsSolutionValueToStringTolerance);
233+
// Don't invent names locallty: if none exist, then indicate this
234+
// - so that the (sparse) solution line format remains "name value
235+
// (index)"
236+
const std::string name = have_col_names ? lp.col_names_[ix] : "NoName";
228237
ss.str(std::string());
229-
ss << highsFormatToString("%-s %s", lp.col_names_[ix].c_str(), valStr.data());
238+
ss << highsFormatToString("%-s %s", name.c_str(), valStr.data());
230239
if (sparse) ss << highsFormatToString(" %d", int(ix));
231240
ss << "\n";
232241
highsFprintfString(file, log_options, ss.str());
@@ -273,9 +282,9 @@ void writeModelSolution(FILE* file, const HighsLogOptions& log_options,
273282
for (HighsInt ix = 0; ix < lp.num_row_; ix++) {
274283
auto valStr = highsDoubleToString(solution.row_value[ix],
275284
kHighsSolutionValueToStringTolerance);
276-
// Create a row name
277285
ss.str(std::string());
278-
ss << highsFormatToString("%-s %s\n", lp.row_names_[ix].c_str(), valStr.data());
286+
ss << highsFormatToString("%-s %s\n", lp.row_names_[ix].c_str(),
287+
valStr.data());
279288
highsFprintfString(file, log_options, ss.str());
280289
}
281290
}
@@ -296,7 +305,8 @@ void writeModelSolution(FILE* file, const HighsLogOptions& log_options,
296305
auto valStr = highsDoubleToString(solution.col_dual[ix],
297306
kHighsSolutionValueToStringTolerance);
298307
ss.str(std::string());
299-
ss << highsFormatToString("%-s %s\n", lp.col_names_[ix].c_str(), valStr.data());
308+
ss << highsFormatToString("%-s %s\n", lp.col_names_[ix].c_str(),
309+
valStr.data());
300310
highsFprintfString(file, log_options, ss.str());
301311
}
302312
ss.str(std::string());
@@ -306,7 +316,8 @@ void writeModelSolution(FILE* file, const HighsLogOptions& log_options,
306316
auto valStr = highsDoubleToString(solution.row_dual[ix],
307317
kHighsSolutionValueToStringTolerance);
308318
ss.str(std::string());
309-
ss << highsFormatToString("%-s %s\n", lp.row_names_[ix].c_str(), valStr.data());
319+
ss << highsFormatToString("%-s %s\n", lp.row_names_[ix].c_str(),
320+
valStr.data());
310321
highsFprintfString(file, log_options, ss.str());
311322
}
312323
}
@@ -359,8 +370,8 @@ HighsStatus normaliseNames(const HighsLogOptions& log_options, HighsLp& lp) {
359370
if (call_status == HighsStatus::kError) return call_status;
360371
HighsStatus return_status = call_status;
361372
call_status =
362-
normaliseNames(log_options, false, lp.num_row_, lp.row_name_prefix_,
363-
lp.row_name_suffix_, lp.row_names_, lp.row_hash_);
373+
normaliseNames(log_options, false, lp.num_row_, lp.row_name_prefix_,
374+
lp.row_name_suffix_, lp.row_names_, lp.row_hash_);
364375
if (call_status != HighsStatus::kOk) return call_status;
365376
return return_status;
366377
}

0 commit comments

Comments
 (0)