From 9fdc6115adc47c340f5f9c5fb3f73f5398661a26 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Thu, 28 Nov 2024 16:09:37 +0000 Subject: [PATCH 1/2] First draft of HighsSimplexStats --- check/TestLpSolvers.cpp | 18 +++++++++++++ src/Highs.h | 4 +++ src/lp_data/HStruct.h | 15 +++++++++++ src/lp_data/HighsSolve.cpp | 2 +- src/simplex/HApp.h | 4 +++ src/simplex/HEkk.cpp | 41 +++++++++++++++++++++++++++++- src/simplex/HEkk.h | 7 +++++ src/simplex/HighsSimplexAnalysis.h | 1 + 8 files changed, 90 insertions(+), 2 deletions(-) diff --git a/check/TestLpSolvers.cpp b/check/TestLpSolvers.cpp index 72b94594d0..c1470fbb58 100644 --- a/check/TestLpSolvers.cpp +++ b/check/TestLpSolvers.cpp @@ -29,6 +29,7 @@ void testDualObjective(const std::string model) { std::max(1.0, std::fabs(primal_objective)); REQUIRE(relative_primal_dual_gap < 1e-12); } + void testSolver(Highs& highs, const std::string solver, IterationCount& default_iteration_count, const HighsInt int_simplex_strategy = 0) { @@ -641,3 +642,20 @@ TEST_CASE("standard-form-lp", "[highs_lp_solver]") { "maximizing\n"); testStandardForm(highs.getLp()); } + +TEST_CASE("simplex-stats", "[highs_lp_solver]") { + HighsStatus return_status; + + Highs h; + // h.setOptionValue("output_flag", dev_run); + std::string model_file = + std::string(HIGHS_DIR) + "/check/instances/adlittle.mps"; + REQUIRE(h.readModel(model_file) == HighsStatus::kOk); + const HighsSimplexStats& simplex_stats = h.getSimplexStats(); + simplex_stats.report(stdout); + h.reportSimplexStats(stdout); + REQUIRE(h.run() == HighsStatus::kOk); + simplex_stats.report(stdout); + h.reportSimplexStats(stdout); + +} diff --git a/src/Highs.h b/src/Highs.h index 04a4a3ce25..e84558039d 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1215,6 +1215,10 @@ class Highs { static void resetGlobalScheduler(bool blocking = false); // Start of advanced methods for HiGHS MIP solver + + const HighsSimplexStats& getSimplexStats() const { return ekk_instance_.getSimplexStats(); } + void reportSimplexStats(FILE* file) const { ekk_instance_.reportSimplexStats(file); } + /** * @brief Get the hot start basis data from the most recent simplex * solve. Advanced method: for HiGHS MIP solver diff --git a/src/lp_data/HStruct.h b/src/lp_data/HStruct.h index 3963d9df1c..86a2bde39c 100644 --- a/src/lp_data/HStruct.h +++ b/src/lp_data/HStruct.h @@ -154,4 +154,19 @@ struct HighsLinearObjective { void clear(); }; +struct HighsSimplexStats { + bool valid; + HighsInt iteration_count; + HighsInt num_invert; + HighsInt last_invert_num_el; + HighsInt last_factored_basis_num_el; + double col_aq_density; + double row_ep_density; + double row_ap_density; + double row_DSE_density; + void report(FILE* file) const; + void clear(); +}; + + #endif /* LP_DATA_HSTRUCT_H_ */ diff --git a/src/lp_data/HighsSolve.cpp b/src/lp_data/HighsSolve.cpp index f1df5239a3..118b92c54a 100644 --- a/src/lp_data/HighsSolve.cpp +++ b/src/lp_data/HighsSolve.cpp @@ -17,7 +17,7 @@ #include "pdlp/CupdlpWrapper.h" #include "simplex/HApp.h" -// The method below runs simplex or ipx solver on the lp. +// The method below runs simplex, ipx or pdlp solver on the lp. HighsStatus solveLp(HighsLpSolverObject& solver_object, const string message) { HighsStatus return_status = HighsStatus::kOk; HighsStatus call_status; diff --git a/src/simplex/HApp.h b/src/simplex/HApp.h index 5e48ae9cd3..905a290c3a 100644 --- a/src/simplex/HApp.h +++ b/src/simplex/HApp.h @@ -112,6 +112,10 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { // return resetModelStatusAndHighsInfo(solver_object); + // Clear the simplex stats + ekk_instance.clearSimplexStats(); + ekk_instance.simplex_stats_.iteration_count = -ekk_instance.iteration_count_; + // Assumes that the LP has a positive number of rows, since // unconstrained LPs should be solved in solveLp bool positive_num_row = solver_object.lp_.num_row_ > 0; diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index dc30e2cc7e..126b56b364 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -296,6 +296,7 @@ void HEkk::invalidate() { assert(!this->status_.is_permuted); this->status_.initialised_for_solve = false; this->invalidateBasisMatrix(); + this->simplex_stats_.clear(); } void HEkk::invalidateBasisMatrix() { @@ -2082,6 +2083,7 @@ HighsInt HEkk::computeFactor() { // number of updates shouldn't be positive info_.update_count = 0; + simplex_stats_.num_invert++; return rank_deficiency; } @@ -3500,7 +3502,18 @@ HighsStatus HEkk::returnFromEkkSolve(const HighsStatus return_status) { // Note that in timeReporting(1), analysis_.analyse_simplex_time // reverts to its value given by options_ if (analysis_.analyse_simplex_time) analysis_.reportSimplexTimer(); - + simplex_stats_.valid = true; + // Since HEkk::iteration_count_ includes iteration on presolved LP, + // simplex_stats_.iteration_count is initialised to - + // HEkk::iteration_count_ + simplex_stats_.iteration_count += iteration_count_; + // simplex_stats_.num_invert is incremented internally + simplex_stats_.last_invert_num_el = simplex_nla_.factor_.invert_num_el; + simplex_stats_.last_factored_basis_num_el = simplex_nla_.factor_.basis_matrix_num_el; + simplex_stats_.col_aq_density = analysis_.col_aq_density; + simplex_stats_.row_ep_density = analysis_.row_ep_density; + simplex_stats_.row_ap_density = analysis_.row_ap_density; + simplex_stats_.row_DSE_density = analysis_.row_DSE_density; return return_status; } @@ -4406,3 +4419,29 @@ void HEkk::unitBtranResidual(const HighsInt row_out, const HVector& row_ep, residual_norm = max(fabs(residual.array[iRow]), residual_norm); } } + +void HighsSimplexStats::report(FILE* file) const { + fprintf(file, "\nSimplex stats\n"); + fprintf(file, " valid = %d\n", this->valid); + fprintf(file, " iteration_count = %d\n", this->iteration_count); + fprintf(file, " num_invert = %d\n", this->num_invert); + fprintf(file, " last_invert_num_el = %d\n", this->last_invert_num_el); + fprintf(file, " last_factored_basis_num_el = %d\n", this->last_factored_basis_num_el); + fprintf(file, " col_aq_density = %g\n", this->col_aq_density); + fprintf(file, " row_ep_density = %g\n", this->row_ep_density); + fprintf(file, " row_ap_density = %g\n", this->row_ap_density); + fprintf(file, " row_DSE_density = %g\n", this->row_DSE_density); + +} + +void HighsSimplexStats::clear() { + valid = false; + iteration_count = 0; + num_invert = 0; + last_invert_num_el = 0; + last_factored_basis_num_el = 0; + col_aq_density = 0; + row_ep_density = 0; + row_ap_density = 0; + row_DSE_density = 0; +} diff --git a/src/simplex/HEkk.h b/src/simplex/HEkk.h index 3b3da389da..9d54524fbc 100644 --- a/src/simplex/HEkk.h +++ b/src/simplex/HEkk.h @@ -156,6 +156,10 @@ class HEkk { const vector& rowLower, const vector& rowUpper); + const HighsSimplexStats& getSimplexStats() const { return simplex_stats_; } + void clearSimplexStats() { simplex_stats_.clear(); } + void reportSimplexStats(FILE* file) const { simplex_stats_.report(file); } + // Make this private later void chooseSimplexStrategyThreads(const HighsOptions& options, HighsSimplexInfo& info); @@ -171,6 +175,7 @@ class HEkk { const std::string message, const HighsInt alt_debug_level = -1) const; bool debugNlaScalingOk(const HighsLp& lp) const; + // Data members HighsCallback* callback_; HighsOptions* options_; @@ -255,6 +260,8 @@ class HEkk { std::vector bad_basis_change_; std::vector primal_phase1_dual_; + HighsSimplexStats simplex_stats_; + private: bool isUnconstrainedLp(); void initialiseForSolve(); diff --git a/src/simplex/HighsSimplexAnalysis.h b/src/simplex/HighsSimplexAnalysis.h index d7a93d2ca6..db43446f20 100644 --- a/src/simplex/HighsSimplexAnalysis.h +++ b/src/simplex/HighsSimplexAnalysis.h @@ -251,6 +251,7 @@ class HighsSimplexAnalysis { void reportFactorTimer(); void updateInvertFormData(const HFactor& factor); void reportInvertFormData(); + HighsInt numInvert() { return num_invert; } // Control methods to be moved to HEkkControl void dualSteepestEdgeWeightError(const double computed_edge_weight, From 258e8c063e6c517244ac3cad15f814d2494856ac Mon Sep 17 00:00:00 2001 From: JAJHall Date: Thu, 28 Nov 2024 17:00:06 +0000 Subject: [PATCH 2/2] HighsSimplexStats struct gives data about last simplex solve --- check/TestLpSolvers.cpp | 42 ++++++++++++++++++++++++++++++----------- src/Highs.h | 8 ++++++-- src/lp_data/HStruct.h | 5 ++--- src/simplex/HApp.h | 5 ++--- src/simplex/HEkk.cpp | 20 +++++++++++--------- src/simplex/HEkk.h | 7 ++++--- 6 files changed, 56 insertions(+), 31 deletions(-) diff --git a/check/TestLpSolvers.cpp b/check/TestLpSolvers.cpp index c1470fbb58..49da8322b0 100644 --- a/check/TestLpSolvers.cpp +++ b/check/TestLpSolvers.cpp @@ -644,18 +644,38 @@ TEST_CASE("standard-form-lp", "[highs_lp_solver]") { } TEST_CASE("simplex-stats", "[highs_lp_solver]") { - HighsStatus return_status; + HighsStatus return_status; Highs h; - // h.setOptionValue("output_flag", dev_run); - std::string model_file = - std::string(HIGHS_DIR) + "/check/instances/adlittle.mps"; - REQUIRE(h.readModel(model_file) == HighsStatus::kOk); const HighsSimplexStats& simplex_stats = h.getSimplexStats(); - simplex_stats.report(stdout); - h.reportSimplexStats(stdout); - REQUIRE(h.run() == HighsStatus::kOk); - simplex_stats.report(stdout); - h.reportSimplexStats(stdout); - + h.setOptionValue("output_flag", dev_run); + std::string model_file = + std::string(HIGHS_DIR) + "/check/instances/adlittle.mps"; + REQUIRE(h.readModel(model_file) == HighsStatus::kOk); + + REQUIRE(h.run() == HighsStatus::kOk); + REQUIRE(simplex_stats.valid); + REQUIRE(simplex_stats.iteration_count == 0); + REQUIRE(simplex_stats.num_invert == 1); + REQUIRE(simplex_stats.last_invert_num_el > 0); + REQUIRE(simplex_stats.last_factored_basis_num_el > 0); + REQUIRE(simplex_stats.col_aq_density == 0); + REQUIRE(simplex_stats.row_ep_density == 0); + REQUIRE(simplex_stats.row_ap_density == 0); + REQUIRE(simplex_stats.row_DSE_density == 0); + if (dev_run) h.reportSimplexStats(stdout); + + h.clearSolver(); + h.setOptionValue("presolve", kHighsOffString); + REQUIRE(h.run() == HighsStatus::kOk); + REQUIRE(simplex_stats.valid); + REQUIRE(simplex_stats.iteration_count > 0); + REQUIRE(simplex_stats.num_invert > 0); + REQUIRE(simplex_stats.last_invert_num_el > 0); + REQUIRE(simplex_stats.last_factored_basis_num_el > 0); + REQUIRE(simplex_stats.col_aq_density > 0); + REQUIRE(simplex_stats.row_ep_density > 0); + REQUIRE(simplex_stats.row_ap_density > 0); + REQUIRE(simplex_stats.row_DSE_density > 0); + if (dev_run) h.reportSimplexStats(stdout); } diff --git a/src/Highs.h b/src/Highs.h index e84558039d..70d800a5de 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1216,8 +1216,12 @@ class Highs { // Start of advanced methods for HiGHS MIP solver - const HighsSimplexStats& getSimplexStats() const { return ekk_instance_.getSimplexStats(); } - void reportSimplexStats(FILE* file) const { ekk_instance_.reportSimplexStats(file); } + const HighsSimplexStats& getSimplexStats() const { + return ekk_instance_.getSimplexStats(); + } + void reportSimplexStats(FILE* file) const { + ekk_instance_.reportSimplexStats(file); + } /** * @brief Get the hot start basis data from the most recent simplex diff --git a/src/lp_data/HStruct.h b/src/lp_data/HStruct.h index 86a2bde39c..35a0b0ee42 100644 --- a/src/lp_data/HStruct.h +++ b/src/lp_data/HStruct.h @@ -164,9 +164,8 @@ struct HighsSimplexStats { double row_ep_density; double row_ap_density; double row_DSE_density; - void report(FILE* file) const; - void clear(); + void report(FILE* file, const std::string message = "") const; + void initialise(const HighsInt iteration_count_ = 0); }; - #endif /* LP_DATA_HSTRUCT_H_ */ diff --git a/src/simplex/HApp.h b/src/simplex/HApp.h index 905a290c3a..b8de2a3f59 100644 --- a/src/simplex/HApp.h +++ b/src/simplex/HApp.h @@ -112,9 +112,8 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { // return resetModelStatusAndHighsInfo(solver_object); - // Clear the simplex stats - ekk_instance.clearSimplexStats(); - ekk_instance.simplex_stats_.iteration_count = -ekk_instance.iteration_count_; + // Initialise the simplex stats + ekk_instance.initialiseSimplexStats(); // Assumes that the LP has a positive number of rows, since // unconstrained LPs should be solved in solveLp diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index 126b56b364..caa4e641e0 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -296,7 +296,7 @@ void HEkk::invalidate() { assert(!this->status_.is_permuted); this->status_.initialised_for_solve = false; this->invalidateBasisMatrix(); - this->simplex_stats_.clear(); + this->simplex_stats_.initialise(); } void HEkk::invalidateBasisMatrix() { @@ -3509,7 +3509,8 @@ HighsStatus HEkk::returnFromEkkSolve(const HighsStatus return_status) { simplex_stats_.iteration_count += iteration_count_; // simplex_stats_.num_invert is incremented internally simplex_stats_.last_invert_num_el = simplex_nla_.factor_.invert_num_el; - simplex_stats_.last_factored_basis_num_el = simplex_nla_.factor_.basis_matrix_num_el; + simplex_stats_.last_factored_basis_num_el = + simplex_nla_.factor_.basis_matrix_num_el; simplex_stats_.col_aq_density = analysis_.col_aq_density; simplex_stats_.row_ep_density = analysis_.row_ep_density; simplex_stats_.row_ap_density = analysis_.row_ap_density; @@ -4420,23 +4421,24 @@ void HEkk::unitBtranResidual(const HighsInt row_out, const HVector& row_ep, } } -void HighsSimplexStats::report(FILE* file) const { - fprintf(file, "\nSimplex stats\n"); +void HighsSimplexStats::report(FILE* file, std::string message) const { + fprintf(file, "\nSimplex stats: %s\n", message.c_str()); fprintf(file, " valid = %d\n", this->valid); fprintf(file, " iteration_count = %d\n", this->iteration_count); fprintf(file, " num_invert = %d\n", this->num_invert); - fprintf(file, " last_invert_num_el = %d\n", this->last_invert_num_el); - fprintf(file, " last_factored_basis_num_el = %d\n", this->last_factored_basis_num_el); + fprintf(file, " last_invert_num_el = %d\n", + this->last_invert_num_el); + fprintf(file, " last_factored_basis_num_el = %d\n", + this->last_factored_basis_num_el); fprintf(file, " col_aq_density = %g\n", this->col_aq_density); fprintf(file, " row_ep_density = %g\n", this->row_ep_density); fprintf(file, " row_ap_density = %g\n", this->row_ap_density); fprintf(file, " row_DSE_density = %g\n", this->row_DSE_density); - } -void HighsSimplexStats::clear() { +void HighsSimplexStats::initialise(const HighsInt iteration_count_) { valid = false; - iteration_count = 0; + iteration_count = -iteration_count_; num_invert = 0; last_invert_num_el = 0; last_factored_basis_num_el = 0; diff --git a/src/simplex/HEkk.h b/src/simplex/HEkk.h index 9d54524fbc..0b253bebc0 100644 --- a/src/simplex/HEkk.h +++ b/src/simplex/HEkk.h @@ -157,8 +157,10 @@ class HEkk { const vector& rowUpper); const HighsSimplexStats& getSimplexStats() const { return simplex_stats_; } - void clearSimplexStats() { simplex_stats_.clear(); } - void reportSimplexStats(FILE* file) const { simplex_stats_.report(file); } + void initialiseSimplexStats() { simplex_stats_.initialise(iteration_count_); } + void reportSimplexStats(FILE* file, const std::string message = "") const { + simplex_stats_.report(file, message); + } // Make this private later void chooseSimplexStrategyThreads(const HighsOptions& options, @@ -175,7 +177,6 @@ class HEkk { const std::string message, const HighsInt alt_debug_level = -1) const; bool debugNlaScalingOk(const HighsLp& lp) const; - // Data members HighsCallback* callback_; HighsOptions* options_;