Skip to content

Commit

Permalink
HiGHS now reads MIPLIB solution file format
Browse files Browse the repository at this point in the history
  • Loading branch information
jajhall committed Nov 1, 2024
1 parent ca8ac9b commit 3202278
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 19 deletions.
49 changes: 48 additions & 1 deletion check/TestCheckSolution.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <cstdio>
//#include <cstdio>
#include <iostream>

#include "HCheckConfig.h"
#include "Highs.h"
Expand Down Expand Up @@ -395,6 +396,52 @@ TEST_CASE("check-set-illegal-solution", "[highs_check_solution]") {
REQUIRE(highs.setSolution(solution) == HighsStatus::kOk);
}

TEST_CASE("read-miplib-solution", "[highs_check_solution]") {
HighsLp lp;
lp.num_col_ = 5;
lp.num_row_ = 1;
lp.sense_ = ObjSense::kMaximize;
lp.col_cost_ = {8, 5, 3, 11, 7};
lp.col_lower_.assign(lp.num_col_, 0);
lp.col_upper_.assign(lp.num_col_, 1);
lp.integrality_.assign(lp.num_col_, HighsVarType::kInteger);
lp.row_lower_ = {-kHighsInf};
lp.row_upper_ = {11};
lp.a_matrix_.format_ = MatrixFormat::kRowwise;
lp.a_matrix_.start_ = {0, 5};
lp.a_matrix_.index_ = {0, 1, 2, 3, 4};
lp.a_matrix_.value_ = {4, 3, 1, 5, 4};
Highs h;
h.setOptionValue("output_flag", dev_run);
h.setOptionValue("presolve", kHighsOffString);
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
REQUIRE(h.run() == HighsStatus::kOk);
// REQUIRE(h.writeSolution("", kSolutionStylePretty) == HighsStatus::kOk);
const std::vector<double>& col_value = h.getSolution().col_value;
std::string miplib_sol_file = "miplib.sol";
FILE* file = fopen(miplib_sol_file.c_str(), "w");
REQUIRE(file != 0);
fprintf(file, "=obj= 22\n");
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
std::string col_name = "c" + std::to_string(int(iCol));
lp.col_names_.push_back(col_name);
if (std::fabs(col_value[iCol]) < 1e-2) continue;
std::string line = col_name + " 1\n";
fprintf(file, "%s", line.c_str());
}
fclose(file);
// Can't read file yet, as model has no column names
REQUIRE(h.readSolution(miplib_sol_file) == HighsStatus::kError);

// Pass model again now that column names have been defined

REQUIRE(h.passModel(lp) == HighsStatus::kOk);
// REQUIRE(h.writeModel("miplib.mps") == HighsStatus::kOk);
REQUIRE(h.readSolution(miplib_sol_file) == HighsStatus::kOk);
REQUIRE(h.run() == HighsStatus::kOk);
std::remove(miplib_sol_file.c_str());
}

void runWriteReadCheckSolution(Highs& highs, const std::string model,
const HighsModelStatus require_model_status,
const HighsInt write_solution_style) {
Expand Down
4 changes: 2 additions & 2 deletions src/lp_data/Highs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3283,7 +3283,7 @@ HighsStatus Highs::readSolution(const std::string& filename,

HighsStatus Highs::assessPrimalSolution(bool& valid, bool& integral,
bool& feasible) const {
return assessLpPrimalSolution(options_, model_.lp_, solution_, valid,
return assessLpPrimalSolution("", options_, model_.lp_, solution_, valid,
integral, feasible);
}

Expand Down Expand Up @@ -3568,7 +3568,7 @@ HighsStatus Highs::completeSolutionFromDiscreteAssignment() {
bool valid, integral, feasible;
// Determine whether this solution is integer feasible
HighsStatus return_status = assessLpPrimalSolution(
options_, lp, solution_, valid, integral, feasible);
"", options_, lp, solution_, valid, integral, feasible);
assert(return_status != HighsStatus::kError);
assert(valid);
// If the current solution is integer feasible, then it can be
Expand Down
48 changes: 35 additions & 13 deletions src/lp_data/HighsLpUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2028,7 +2028,7 @@ void analyseLp(const HighsLogOptions& log_options, const HighsLp& lp) {
}

HighsStatus readSolutionFile(const std::string filename,
const HighsOptions& options, const HighsLp& lp,
const HighsOptions& options, HighsLp& lp,
HighsBasis& basis, HighsSolution& solution,
const HighsInt style) {
const HighsLogOptions& log_options = options.log_options;
Expand Down Expand Up @@ -2068,10 +2068,19 @@ HighsStatus readSolutionFile(const std::string filename,
return readSolutionFileErrorReturn(
in_file); // Model (status) or =obj= (value)
const bool miplib_sol = section_name == "=obj=";
printf(
"Read \"%s\" as first term of first line of solution file: miplib_sol = "
"%d\n",
section_name.c_str(), miplib_sol);
if (miplib_sol) {
// A MIPLIB solution file has nonzero solution values for a subset
// of the variables identified by name, so there must be column
// names
if (!lp.col_names_.size()) {
highsLogUser(log_options, HighsLogType::kError,
"readSolutionFile: Cannot read a MIPLIB solution file "
"without column names in the model\n");
return HighsStatus::kError;
}
// Ensure that the col name hash table has been formed
if (!lp.col_hash_.name2index.size()) lp.col_hash_.form(lp.col_names_);
}
bool sparse = false;
if (!miplib_sol) {
if (!readSolutionFileIgnoreLineOk(in_file))
Expand Down Expand Up @@ -2116,15 +2125,26 @@ HighsStatus readSolutionFile(const std::string filename,
}
}
if (miplib_sol) {
HighsInt num_value = 0;
read_solution.col_value.assign(lp_num_col, 0);
for (;;) {
if (!readSolutionFileIdDoubleLineOk(name, value, in_file))
return readSolutionFileErrorReturn(in_file);
// Need to be able to hash names
HighsStatus status = HighsStatus::kError;
HighsInt iCol = 0;
// Only false return is for encountering EOF
if (!readSolutionFileIdDoubleLineOk(name, value, in_file)) break;
auto search = lp.col_hash_.name2index.find(name);
if (search == lp.col_hash_.name2index.end()) {
highsLogUser(log_options, HighsLogType::kError,
"readSolutionFile: name %s is not found\n", name.c_str());
return HighsStatus::kError;
} else if (search->second == kHashIsDuplicate) {
highsLogUser(log_options, HighsLogType::kError,
"readSolutionFile: name %s is duplicated\n", name.c_str());
return HighsStatus::kError;
}
HighsInt iCol = search->second;
assert(lp.col_names_[iCol] == name);
read_solution.col_value[iCol] = value;
assert(123 == 456);
num_value++;
if (in_file.eof()) break;
}
} else if (sparse) {
read_solution.col_value.assign(lp_num_col, 0);
Expand Down Expand Up @@ -2352,7 +2372,8 @@ void assessColPrimalSolution(const HighsOptions& options, const double primal,

// Determine validity, primal feasibility and (when relevant) integer
// feasibility of a solution
HighsStatus assessLpPrimalSolution(const HighsOptions& options,
HighsStatus assessLpPrimalSolution(const std::string message,
const HighsOptions& options,
const HighsLp& lp,
const HighsSolution& solution, bool& valid,
bool& integral, bool& feasible) {
Expand All @@ -2377,7 +2398,8 @@ HighsStatus assessLpPrimalSolution(const HighsOptions& options,
lp.isMip() ? options.mip_feasibility_tolerance
: options.primal_feasibility_tolerance;
highsLogUser(options.log_options, HighsLogType::kInfo,
"Assessing feasibility of %s tolerance of %11.4g\n",
"%sAssessing feasibility of %s tolerance of %11.4g\n",
message.c_str(),
lp.isMip() ? "MIP using primal feasibility and integrality"
: "LP using primal feasibility",
kPrimalFeasibilityTolerance);
Expand Down
5 changes: 3 additions & 2 deletions src/lp_data/HighsLpUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ void getLpMatrixCoefficient(const HighsLp& lp, const HighsInt row,
void analyseLp(const HighsLogOptions& log_options, const HighsLp& lp);

HighsStatus readSolutionFile(const std::string filename,
const HighsOptions& options, const HighsLp& lp,
const HighsOptions& options, HighsLp& lp,
HighsBasis& basis, HighsSolution& solution,
const HighsInt style);

Expand All @@ -227,7 +227,8 @@ void assessColPrimalSolution(const HighsOptions& options, const double primal,
const HighsVarType type, double& col_infeasibility,
double& integer_infeasibility);

HighsStatus assessLpPrimalSolution(const HighsOptions& options,
HighsStatus assessLpPrimalSolution(const std::string message,
const HighsOptions& options,
const HighsLp& lp,
const HighsSolution& solution, bool& valid,
bool& integral, bool& feasible);
Expand Down
3 changes: 2 additions & 1 deletion src/mip/HighsMipSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ HighsMipSolver::HighsMipSolver(HighsCallback& callback,
// so validate using assert
#ifndef NDEBUG
bool valid, integral, feasible;
assessLpPrimalSolution(options, lp, solution, valid, integral, feasible);
assessLpPrimalSolution("For debugging: ", options, lp, solution, valid,
integral, feasible);
assert(valid);
#endif
bound_violation_ = 0;
Expand Down

0 comments on commit 3202278

Please sign in to comment.