Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added writePresolvedModel and unit test #1741

Merged
merged 2 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions check/TestPresolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -575,3 +575,29 @@ TEST_CASE("postsolve-reduced-to-empty", "[highs_test_presolve]") {
REQUIRE(highs.getInfo().num_primal_infeasibilities == 0);
REQUIRE(highs.getInfo().num_dual_infeasibilities == 0);
}

TEST_CASE("write-presolved-model", "[highs_test_presolve]") {
std::string presolved_model_file = "temp.mps";
std::string model_file =
std::string(HIGHS_DIR) + "/check/instances/afiro.mps";
Highs highs;
highs.setOptionValue("output_flag", dev_run);
REQUIRE(highs.readModel(model_file) == HighsStatus::kOk);
highs.presolve();
highs.writePresolvedModel(presolved_model_file);
// Read and solve the presolved model using a new Highs instance
Highs highs1;
highs1.setOptionValue("output_flag", dev_run);
highs1.readModel(presolved_model_file);
highs1.run();
// Extract the optimal solution and basis
HighsSolution solution = highs1.getSolution();
HighsBasis basis = highs1.getBasis();
// Perform postsolve using the optimal solution and basis for the
// presolved model
highs.postsolve(solution, basis);
// The solution should be optimal, so no solver is run, and
// simplex_iteration_count is -1
REQUIRE(highs.getInfo().simplex_iteration_count == -1);
std::remove(presolved_model_file.c_str());
}
11 changes: 11 additions & 0 deletions src/Highs.h
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,17 @@ class Highs {
*/
HighsStatus writeModel(const std::string& filename = "");

/**
* @brief Write out the incumbent presolved model to a file
*/
HighsStatus writePresolvedModel(const std::string& filename = "");

/**
* @brief Write out the given model to a file
*/
HighsStatus writeLocalModel(HighsModel& model,
const std::string& filename = "");

/**
* @brief Write out the internal HighsBasis instance to a file
*/
Expand Down
1 change: 1 addition & 0 deletions src/highs_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,7 @@ PYBIND11_MODULE(_core, m) {
.def("getRowByName", &highs_getRowByName)

.def("writeModel", &Highs::writeModel)
.def("writePresolvedModel", &Highs::writePresolvedModel)
.def("crossover", &Highs::crossover)
.def("changeObjectiveSense", &Highs::changeObjectiveSense)
.def("changeObjectiveOffset", &Highs::changeObjectiveOffset)
Expand Down
4 changes: 4 additions & 0 deletions src/interfaces/highs_c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ HighsInt Highs_writeModel(void* highs, const char* filename) {
return (HighsInt)((Highs*)highs)->writeModel(std::string(filename));
}

HighsInt Highs_writePresolvedModel(void* highs, const char* filename) {
return (HighsInt)((Highs*)highs)->writePresolvedModel(std::string(filename));
}

HighsInt Highs_writeSolution(const void* highs, const char* filename) {
return (HighsInt)((Highs*)highs)
->writeSolution(std::string(filename), kSolutionStyleRaw);
Expand Down
10 changes: 10 additions & 0 deletions src/interfaces/highs_c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,16 @@ HighsInt Highs_readModel(void* highs, const char* filename);
*/
HighsInt Highs_writeModel(void* highs, const char* filename);

/**
* Write the presolved model in `highs` to `filename`.
*
* @param highs A pointer to the Highs instance.
* @param filename The filename to write.
*
* @returns A `kHighsStatus` constant indicating whether the call succeeded.
*/
HighsInt Highs_writePresolvedModel(void* highs, const char* filename);

/**
* Reset the options and then call `clearModel`.
*
Expand Down
10 changes: 9 additions & 1 deletion src/interfaces/highs_csharp_api.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ public class HighsLpSolver : IDisposable
[DllImport(highslibname)]
private static extern int Highs_writeModel(IntPtr highs, string filename);

[DllImport(highslibname)]
private static extern int Highs_writePresolvedModel(IntPtr highs, string filename);

[DllImport(highslibname)]
private static extern int Highs_writeSolutionPretty(IntPtr highs, string filename);

Expand Down Expand Up @@ -597,6 +600,11 @@ public HighsStatus writeModel(string filename)
return (HighsStatus)HighsLpSolver.Highs_writeModel(this.highs, filename);
}

public HighsStatus writePresolvedModel(string filename)
{
return (HighsStatus)HighsLpSolver.Highs_writePresolvedModel(this.highs, filename);
}

public HighsStatus writeSolutionPretty(string filename)
{
return (HighsStatus)HighsLpSolver.Highs_writeSolutionPretty(this.highs, filename);
Expand Down Expand Up @@ -1028,4 +1036,4 @@ public class SolutionInfo
/// </summary>
public double ObjectiveValue { get; set; }
}
}
}
7 changes: 7 additions & 0 deletions src/interfaces/highs_fortran_api.f90
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ function Highs_writeModel ( h, f ) result ( s ) bind ( c, name='Highs_writeModel
integer ( c_int ) :: s
end function Highs_writeModel

function Highs_writePresolvedModel ( h, f ) result ( s ) bind ( c, name='Highs_writePresolvedModel' )
use iso_c_binding
type(c_ptr), VALUE :: h
character( c_char ) :: f(*)
integer ( c_int ) :: s
end function Highs_writePresolvedModel

function Highs_writeSolution ( h, f ) result ( s ) bind ( c, name='Highs_writeSolution' )
use iso_c_binding
type(c_ptr), VALUE :: h
Expand Down
23 changes: 16 additions & 7 deletions src/lp_data/Highs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,17 +676,26 @@ HighsStatus Highs::readBasis(const std::string& filename) {
}

HighsStatus Highs::writeModel(const std::string& filename) {
return writeLocalModel(model_, filename);
}

HighsStatus Highs::writePresolvedModel(const std::string& filename) {
return writeLocalModel(presolved_model_, filename);
}

HighsStatus Highs::writeLocalModel(HighsModel& model,
const std::string& filename) {
HighsStatus return_status = HighsStatus::kOk;

// Ensure that the LP is column-wise
model_.lp_.ensureColwise();
model.lp_.ensureColwise();
// Check for repeated column or row names that would corrupt the file
if (model_.lp_.col_hash_.hasDuplicate(model_.lp_.col_names_)) {
if (model.lp_.col_hash_.hasDuplicate(model.lp_.col_names_)) {
highsLogUser(options_.log_options, HighsLogType::kError,
"Model has repeated column names\n");
return returnFromHighs(HighsStatus::kError);
}
if (model_.lp_.row_hash_.hasDuplicate(model_.lp_.row_names_)) {
if (model.lp_.row_hash_.hasDuplicate(model.lp_.row_names_)) {
highsLogUser(options_.log_options, HighsLogType::kError,
"Model has repeated row names\n");
return returnFromHighs(HighsStatus::kError);
Expand All @@ -706,10 +715,10 @@ HighsStatus Highs::writeModel(const std::string& filename) {
// Report to user that model is being written
highsLogUser(options_.log_options, HighsLogType::kInfo,
"Writing the model to %s\n", filename.c_str());
return_status = interpretCallStatus(
options_.log_options,
writer->writeModelToFile(options_, filename, model_), return_status,
"writeModelToFile");
return_status =
interpretCallStatus(options_.log_options,
writer->writeModelToFile(options_, filename, model),
return_status, "writeModelToFile");
delete writer;
}
return returnFromHighs(return_status);
Expand Down