From 5415d179a8d13190abbd66a1dca24574750df3af Mon Sep 17 00:00:00 2001 From: jajhall Date: Thu, 2 May 2024 13:37:35 +0100 Subject: [PATCH 1/2] Added writePresolvedModel and unit test --- check/TestPresolve.cpp | 26 ++++++++++++++++++++++++++ src/Highs.h | 11 +++++++++++ src/interfaces/highs_c_api.cpp | 4 ++++ src/interfaces/highs_c_api.h | 10 ++++++++++ src/lp_data/Highs.cpp | 23 ++++++++++++++++------- 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/check/TestPresolve.cpp b/check/TestPresolve.cpp index 31f7f99a63..86c5dfc469 100644 --- a/check/TestPresolve.cpp +++ b/check/TestPresolve.cpp @@ -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()); +} diff --git a/src/Highs.h b/src/Highs.h index 62d4eca66e..8bd4cd30b3 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -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 */ diff --git a/src/interfaces/highs_c_api.cpp b/src/interfaces/highs_c_api.cpp index 6ead1b06e1..be4b9f6183 100644 --- a/src/interfaces/highs_c_api.cpp +++ b/src/interfaces/highs_c_api.cpp @@ -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); diff --git a/src/interfaces/highs_c_api.h b/src/interfaces/highs_c_api.h index f803a92c73..5816dee63b 100644 --- a/src/interfaces/highs_c_api.h +++ b/src/interfaces/highs_c_api.h @@ -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`. * diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index 482ab3f47c..e68863c03b 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -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); @@ -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); From 3493d103390ebbc66367b9e6064749e9d5ed69fc Mon Sep 17 00:00:00 2001 From: jajhall Date: Thu, 2 May 2024 13:43:13 +0100 Subject: [PATCH 2/2] Added writePresolvedModel to C# FORTRAN and Python interfaces --- src/highs_bindings.cpp | 1 + src/interfaces/highs_csharp_api.cs | 10 +++++++++- src/interfaces/highs_fortran_api.f90 | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/highs_bindings.cpp b/src/highs_bindings.cpp index 85cd1728f6..f6b5e8e802 100644 --- a/src/highs_bindings.cpp +++ b/src/highs_bindings.cpp @@ -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) diff --git a/src/interfaces/highs_csharp_api.cs b/src/interfaces/highs_csharp_api.cs index c0da92697b..0756cc02ea 100644 --- a/src/interfaces/highs_csharp_api.cs +++ b/src/interfaces/highs_csharp_api.cs @@ -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); @@ -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); @@ -1028,4 +1036,4 @@ public class SolutionInfo /// public double ObjectiveValue { get; set; } } -} \ No newline at end of file +} diff --git a/src/interfaces/highs_fortran_api.f90 b/src/interfaces/highs_fortran_api.f90 index 05e2d17b1b..7014907f7a 100644 --- a/src/interfaces/highs_fortran_api.f90 +++ b/src/interfaces/highs_fortran_api.f90 @@ -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