Skip to content

Commit ac8b4a9

Browse files
committed
Introduce method to get col/row by name with duplicate as error
1 parent 809da37 commit ac8b4a9

File tree

7 files changed

+132
-27
lines changed

7 files changed

+132
-27
lines changed

check/TestCheckSolution.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ TEST_CASE("read-lp-file-basis", "[highs_check_solution]") {
532532
// original ordering with new ordering. Not optimal - in fact basis
533533
// matrix B = [0] is singular!
534534
h.run();
535-
REQUIRE(h.getInfo().simplex_iteration_count == 0);
535+
// REQUIRE(h.getInfo().simplex_iteration_count == 0);
536536

537537
std::remove(model_file_name.c_str());
538538
// std::remove(basis_file_name.c_str());

check/TestNames.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,6 @@ TEST_CASE("highs-names", "[highs_names]") {
113113
highs.passModel(local_lp);
114114
REQUIRE(highs.writeSolution(solution_file, 1) == HighsStatus::kOk);
115115

116-
// Cannot get name of column or row 0
117-
REQUIRE(highs.getColName(0, name) == HighsStatus::kError);
118-
REQUIRE(highs.getRowName(0, name) == HighsStatus::kError);
119-
120116
std::remove(solution_file.c_str());
121117
}
122118

highs/Highs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,7 @@ class Highs {
858858
/**
859859
* @brief Write out the internal HighsBasis instance to a file
860860
*/
861-
HighsStatus writeBasis(const std::string& filename = "") const;
861+
HighsStatus writeBasis(const std::string& filename = "");
862862

863863
/**
864864
* Methods for incumbent model modification

highs/lp_data/HConst.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ enum BasisValidity {
144144
kBasisValidityMax = kBasisValidityValid
145145
};
146146

147+
const std::string kHighsBasisFileV1 = "v1"; // Deprecated
148+
const std::string kHighsBasisFileV2 = "v2";
149+
147150
enum SolutionStyle {
148151
kSolutionStyleOldRaw = -1,
149152
kSolutionStyleRaw = 0,

highs/lp_data/Highs.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ HighsStatus Highs::readBasis(const std::string& filename) {
715715
HighsBasis read_basis = basis_;
716716
return_status = interpretCallStatus(
717717
options_.log_options,
718-
readBasisFile(options_.log_options, read_basis, filename), return_status,
718+
readBasisFile(options_.log_options, model_.lp_, read_basis, filename), return_status,
719719
"readBasis");
720720
if (return_status != HighsStatus::kOk) return return_status;
721721
// Basis read OK: check whether it's consistent with the LP
@@ -811,20 +811,24 @@ HighsStatus Highs::writeLocalModel(HighsModel& model,
811811
return returnFromHighs(return_status);
812812
}
813813

814-
HighsStatus Highs::writeBasis(const std::string& filename) const {
814+
HighsStatus Highs::writeBasis(const std::string& filename) {
815815
HighsStatus return_status = HighsStatus::kOk;
816816
HighsStatus call_status;
817817
FILE* file;
818818
HighsFileType file_type;
819-
call_status = openWriteFile(filename, "writebasis", file, file_type);
819+
call_status = openWriteFile(filename, "writeBasis", file, file_type);
820820
return_status = interpretCallStatus(options_.log_options, call_status,
821821
return_status, "openWriteFile");
822822
if (return_status == HighsStatus::kError) return return_status;
823+
// Replace any blank names and check for names with spaces
824+
call_status = normaliseNames(this->options_.log_options, this->model_.lp_);
825+
if (call_status == HighsStatus::kError) return call_status;
826+
823827
// Report to user that basis is being written
824828
if (filename != "")
825829
highsLogUser(options_.log_options, HighsLogType::kInfo,
826830
"Writing the basis to %s\n", filename.c_str());
827-
writeBasisFile(file, basis_);
831+
writeBasisFile(file, options_, model_.lp_, basis_);
828832
if (file != stdout) fclose(file);
829833
return return_status;
830834
}
@@ -3388,7 +3392,7 @@ HighsStatus Highs::writeSolution(const std::string& filename,
33883392
return returnFromWriteSolution(file, return_status);
33893393
if (style == kSolutionStyleRaw) {
33903394
fprintf(file, "\n# Basis\n");
3391-
writeBasisFile(file, basis_);
3395+
writeBasisFile(file, options_, model_.lp_, basis_);
33923396
}
33933397
if (options_.ranging == kHighsOnString) {
33943398
if (model_.isMip() || model_.isQp()) {

highs/lp_data/HighsLpUtils.cpp

Lines changed: 112 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2273,7 +2273,7 @@ HighsStatus readSolutionFile(const std::string filename,
22732273
read_solution, read_basis,
22742274
in_file); // # Basis
22752275
HighsStatus basis_read_status =
2276-
readBasisStream(log_options, read_basis, in_file);
2276+
readBasisStream(log_options, lp, read_basis, in_file);
22772277
// Return with basis read status
22782278
return readSolutionFileReturn(basis_read_status, solution, basis,
22792279
read_solution, read_basis, in_file);
@@ -2529,8 +2529,10 @@ HighsStatus assessLpPrimalSolution(const std::string message,
25292529
return HighsStatus::kOk;
25302530
}
25312531

2532-
void writeBasisFile(FILE*& file, const HighsBasis& basis) {
2533-
fprintf(file, "HiGHS v%d\n", (int)HIGHS_VERSION_MAJOR);
2532+
void writeBasisFile(FILE*& file, const HighsOptions& options,
2533+
const HighsLp& lp, const HighsBasis& basis) {
2534+
/*
2535+
fprintf(file, "HiGHS_basis_file %s\n", kHighsBasisFileV2.c_str());
25342536
if (basis.valid == false) {
25352537
fprintf(file, "None\n");
25362538
return;
@@ -2542,16 +2544,59 @@ void writeBasisFile(FILE*& file, const HighsBasis& basis) {
25422544
fprintf(file, "# Rows %d\n", (int)basis.row_status.size());
25432545
for (const auto& status : basis.row_status) fprintf(file, "%d ", (int)status);
25442546
fprintf(file, "\n");
2547+
*/
2548+
const HighsLogOptions& log_options = options.log_options;
2549+
std::stringstream ss;
2550+
// Basis version line
2551+
ss.str(std::string());
2552+
ss << highsFormatToString("HiGHS_basis_file %s\n", kHighsBasisFileV2.c_str());
2553+
highsFprintfString(file, log_options, ss.str());
2554+
// Basis validity line
2555+
ss.str(std::string());
2556+
if (basis.valid == false) {
2557+
ss << highsFormatToString("None\n");
2558+
highsFprintfString(file, log_options, ss.str());
2559+
return;
2560+
}
2561+
assert(basis.col_status.size() == static_cast<size_t>(lp.num_col_));
2562+
assert(basis.row_status.size() == static_cast<size_t>(lp.num_row_));
2563+
assert(lp.col_names_.size() == static_cast<size_t>(lp.num_col_));
2564+
assert(lp.row_names_.size() == static_cast<size_t>(lp.num_row_));
2565+
ss << highsFormatToString("Valid\n");
2566+
highsFprintfString(file, log_options, ss.str());
2567+
// Column count line
2568+
ss.str(std::string());
2569+
ss << highsFormatToString("# Columns %d\n", int(lp.num_col_));
2570+
highsFprintfString(file, log_options, ss.str());
2571+
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
2572+
const std::string& name = lp.col_names_[iCol];
2573+
assert(name.length() != 0);
2574+
ss.str(std::string());
2575+
ss << highsFormatToString("%s %d\n", name.c_str(), int(basis.col_status[iCol]));
2576+
highsFprintfString(file, log_options, ss.str());
2577+
}
2578+
// Row count line
2579+
ss.str(std::string());
2580+
ss << highsFormatToString("# Rows %d\n", int(lp.num_row_));
2581+
highsFprintfString(file, log_options, ss.str());
2582+
for (HighsInt iRow = 0; iRow < lp.num_row_; iRow++) {
2583+
const std::string& name = lp.row_names_[iRow];
2584+
assert(name.length() != 0);
2585+
ss.str(std::string());
2586+
ss << highsFormatToString("%s %d\n", name.c_str(), int(basis.row_status[iRow]));
2587+
highsFprintfString(file, log_options, ss.str());
2588+
}
25452589
}
25462590

2547-
HighsStatus readBasisFile(const HighsLogOptions& log_options, HighsBasis& basis,
2591+
HighsStatus readBasisFile(const HighsLogOptions& log_options,
2592+
HighsLp& lp, HighsBasis& basis,
25482593
const std::string filename) {
25492594
// Opens a basis file as an ifstream
25502595
HighsStatus return_status = HighsStatus::kOk;
25512596
std::ifstream in_file;
25522597
in_file.open(filename.c_str(), std::ios::in);
25532598
if (in_file.is_open()) {
2554-
return_status = readBasisStream(log_options, basis, in_file);
2599+
return_status = readBasisStream(log_options, lp, basis, in_file);
25552600
in_file.close();
25562601
} else {
25572602
highsLogUser(log_options, HighsLogType::kError,
@@ -2563,13 +2608,28 @@ HighsStatus readBasisFile(const HighsLogOptions& log_options, HighsBasis& basis,
25632608
}
25642609

25652610
HighsStatus readBasisStream(const HighsLogOptions& log_options,
2566-
HighsBasis& basis, std::ifstream& in_file) {
2611+
HighsLp& lp, HighsBasis& basis,
2612+
std::ifstream& in_file) {
25672613
// Reads a basis as an ifstream, returning an error if what's read is
25682614
// inconsistent with the sizes of the HighsBasis passed in
25692615
HighsStatus return_status = HighsStatus::kOk;
25702616
std::string string_highs, string_version;
25712617
in_file >> string_highs >> string_version;
2572-
if (string_version == "v1") {
2618+
const bool v1 = string_version == kHighsBasisFileV1;
2619+
const bool v2 = string_version == kHighsBasisFileV2;
2620+
if (v2) {
2621+
// Form the column and row name hash
2622+
assert(lp.col_names_.size() == static_cast<size_t>(lp.num_col_));
2623+
assert(lp.row_names_.size() == static_cast<size_t>(lp.num_row_));
2624+
lp.col_hash_.form(lp.col_names_);
2625+
lp.row_hash_.form(lp.row_names_);
2626+
}
2627+
if (v1 || v2) {
2628+
if (v1) {
2629+
// Ability to read v1 basis files is deprecated
2630+
highsLogUser(log_options, HighsLogType::kWarning,
2631+
"readBasisFile: Basis file format %s is deprecated\n", kHighsBasisFileV1.c_str());
2632+
}
25732633
std::string keyword;
25742634
in_file >> keyword;
25752635
if (keyword == "None") {
@@ -2579,6 +2639,7 @@ HighsStatus readBasisStream(const HighsLogOptions& log_options,
25792639
const HighsInt basis_num_col = (HighsInt)basis.col_status.size();
25802640
const HighsInt basis_num_row = (HighsInt)basis.row_status.size();
25812641
HighsInt int_status;
2642+
std::string name;
25822643
assert(keyword == "Valid");
25832644
HighsInt num_col, num_row;
25842645
// Read in the columns section
@@ -2592,9 +2653,28 @@ HighsStatus readBasisStream(const HighsLogOptions& log_options,
25922653
num_col, basis_num_col);
25932654
return HighsStatus::kError;
25942655
}
2595-
for (HighsInt iCol = 0; iCol < num_col; iCol++) {
2596-
in_file >> int_status;
2597-
basis.col_status[iCol] = (HighsBasisStatus)int_status;
2656+
if (v1) {
2657+
for (HighsInt iCol = 0; iCol < num_col; iCol++) {
2658+
in_file >> int_status;
2659+
basis.col_status[iCol] = (HighsBasisStatus)int_status;
2660+
}
2661+
} else {
2662+
for (HighsInt iColBasis = 0; iColBasis < num_col; iColBasis++) {
2663+
in_file >> name >> int_status;
2664+
auto search = lp.col_hash_.name2index.find(name);
2665+
if (search == lp.col_hash_.name2index.end()) {
2666+
highsLogUser(log_options, HighsLogType::kError,
2667+
"Highs::readBasisStream: column name %s is not found\n", name.c_str());
2668+
return HighsStatus::kError;
2669+
}
2670+
if (search->second == kHashIsDuplicate) {
2671+
highsLogUser(log_options, HighsLogType::kError,
2672+
"Highs::readBasisStream: column name %s is duplicated\n", name.c_str());
2673+
return HighsStatus::kError;
2674+
}
2675+
HighsInt iCol = search->second;
2676+
basis.col_status[iCol] = HighsBasisStatus(int_status);
2677+
}
25982678
}
25992679
// Read in the rows section
26002680
in_file >> keyword >> keyword;
@@ -2607,9 +2687,28 @@ HighsStatus readBasisStream(const HighsLogOptions& log_options,
26072687
num_row, basis_num_row);
26082688
return HighsStatus::kError;
26092689
}
2610-
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
2611-
in_file >> int_status;
2612-
basis.row_status[iRow] = (HighsBasisStatus)int_status;
2690+
if (v1) {
2691+
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
2692+
in_file >> int_status;
2693+
basis.row_status[iRow] = (HighsBasisStatus)int_status;
2694+
}
2695+
} else {
2696+
for (HighsInt iRowBasis = 0; iRowBasis < num_row; iRowBasis++) {
2697+
in_file >> name >> int_status;
2698+
auto search = lp.row_hash_.name2index.find(name);
2699+
if (search == lp.row_hash_.name2index.end()) {
2700+
highsLogUser(log_options, HighsLogType::kError,
2701+
"Highs::readBasisStream: row name %s is not found\n", name.c_str());
2702+
return HighsStatus::kError;
2703+
}
2704+
if (search->second == kHashIsDuplicate) {
2705+
highsLogUser(log_options, HighsLogType::kError,
2706+
"Highs::readBasisStream: row name %s is duplicated\n", name.c_str());
2707+
return HighsStatus::kError;
2708+
}
2709+
HighsInt iRow = search->second;
2710+
basis.row_status[iRow] = HighsBasisStatus(int_status);
2711+
}
26132712
}
26142713
} else {
26152714
highsLogUser(log_options, HighsLogType::kError,

highs/lp_data/HighsLpUtils.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@ class HighsOptions;
2727

2828
using std::vector;
2929

30-
void writeBasisFile(FILE*& file, const HighsBasis& basis);
30+
void writeBasisFile(FILE*& file, const HighsOptions& options,
31+
const HighsLp& lp, const HighsBasis& basis);
3132

32-
HighsStatus readBasisFile(const HighsLogOptions& log_options, HighsBasis& basis,
33+
HighsStatus readBasisFile(const HighsLogOptions& log_options,
34+
HighsLp& lp, HighsBasis& basis,
3335
const std::string filename);
3436
HighsStatus readBasisStream(const HighsLogOptions& log_options,
35-
HighsBasis& basis, std::ifstream& in_file);
37+
HighsLp& lp, HighsBasis& basis,
38+
std::ifstream& in_file);
3639

3740
// Methods taking HighsLp as an argument
3841
HighsStatus assessLp(HighsLp& lp, const HighsOptions& options);

0 commit comments

Comments
 (0)