From fdc149a9b435dbd3fab5ba25d9af361fd1831a0d Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 25 Nov 2024 15:51:54 +0000 Subject: [PATCH 01/40] Make analytic centre calculation an option - allowing true calculation - and count failures --- src/ipm/ipx/conjugate_residuals.cc | 11 +++++++++++ src/ipm/ipx/lp_solver.cc | 20 ++++++++++---------- src/mip/HighsMipSolverData.cpp | 29 +++++++++++++++++++---------- src/mip/HighsMipSolverData.h | 2 ++ 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/ipm/ipx/conjugate_residuals.cc b/src/ipm/ipx/conjugate_residuals.cc index 9fe770e3b9..eb25be7118 100644 --- a/src/ipm/ipx/conjugate_residuals.cc +++ b/src/ipm/ipx/conjugate_residuals.cc @@ -26,6 +26,12 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, if (maxiter < 0) maxiter = m+100; + Int putative_large_iter = 100;//std::max(1000, maxiter/100); + maxiter = putative_large_iter; + Int large_iter = 0;//std::max(1000, maxiter/100); + Int report_frequency = 1;//100; + Int report_iter = 0; + // Initialize residual, step and Cstep. if (Infnorm(lhs) == 0.0) { residual = rhs; // saves a matrix-vector op @@ -59,6 +65,11 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, errflag_ = IPX_ERROR_cr_matrix_not_posdef; break; } + if (iter_ > large_iter && iter_ > report_iter + report_frequency) { + printf("ConjugateResiduals::Solve Iteration count (large = %d; max = %d) is %d\n", int(putative_large_iter), int(maxiter), int(iter_)); + report_iter = iter_; + report_frequency *= 2; + } // Update lhs, residual and Cresidual. const double denom = Dot(Cstep,Cstep); diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index 145930c75b..6fb771587d 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -65,16 +65,16 @@ Int LpSolver::Solve() { // if ((info_.status_ipm == IPX_STATUS_optimal || // info_.status_ipm == IPX_STATUS_imprecise) && run_crossover_on) { if (run_crossover) { - if (run_crossover_on) { - control_.hLog("Running crossover as requested\n"); - } else if (run_crossover_choose) { - assert(info_.status_ipm == IPX_STATUS_imprecise); - control_.hLog("Running crossover since IPX is imprecise\n"); - } else { - assert(run_crossover_on || run_crossover_choose); - } - BuildCrossoverStartingPoint(); - RunCrossover(); + if (run_crossover_on) { + control_.hLog("Running crossover as requested\n"); + } else if (run_crossover_choose) { + assert(info_.status_ipm == IPX_STATUS_imprecise); + control_.hLog("Running crossover since IPX is imprecise\n"); + } else { + assert(run_crossover_on || run_crossover_choose); + } + BuildCrossoverStartingPoint(); + RunCrossover(); } if (basis_) { info_.ftran_sparse = basis_->frac_ftran_sparse(); diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index e84f9b5396..3dd88092c9 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -318,7 +318,8 @@ void HighsMipSolverData::startAnalyticCenterComputation( // "_ipm.mps"; printf("Calling ipm.writeModel(%s)\n", // file_name.c_str()); fflush(stdout); ipm.writeModel(file_name); // } - + printf("HighsMipSolverData::startAnalyticCenterComputation submip = %d analyticCenterFailed = %d; num_Col = %d; num_row = %d\n", + mipsolver.submip, analyticCenterFailed, int(ipm.getNumCol()), int(ipm.getNumRow())); mipsolver.analysis_.mipTimerStart(kMipClockIpmSolveLp); ipm.run(); mipsolver.analysis_.mipTimerStop(kMipClockIpmSolveLp); @@ -345,6 +346,7 @@ void HighsMipSolverData::finishAnalyticCenterComputation( fflush(stdout); } analyticCenterComputed = true; + analyticCenterFailed = analyticCenterStatus == HighsModelStatus::kSolveError; if (analyticCenterStatus == HighsModelStatus::kOptimal) { HighsInt nfixed = 0; HighsInt nintfixed = 0; @@ -611,6 +613,7 @@ void HighsMipSolverData::init() { firstlpsolobj = -kHighsInf; rootlpsolobj = -kHighsInf; analyticCenterComputed = false; + analyticCenterFailed = false; analyticCenterStatus = HighsModelStatus::kNotset; maxTreeSizeLog2 = 0; numRestarts = 0; @@ -963,7 +966,10 @@ void HighsMipSolverData::runSetup() { } #endif - if (upper_limit == kHighsInf) analyticCenterComputed = false; + if (upper_limit == kHighsInf) { + analyticCenterComputed = false; + analyticCenterFailed = false; + } analyticCenterStatus = HighsModelStatus::kNotset; analyticCenter.clear(); @@ -1865,10 +1871,13 @@ void HighsMipSolverData::evaluateRootNode() { startSymmetryDetection(tg, symData); mipsolver.analysis_.mipTimerStop(kMipClockStartSymmetryDetection); } - if (compute_analytic_centre && !analyticCenterComputed) { - highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kInfo, - "MIP-Timing: %11.2g - starting analytic centre calculation\n", - mipsolver.timer_.read()); + if (compute_analytic_centre && !analyticCenterComputed && !analyticCenterFailed) { + if (mipsolver.analysis_.analyse_mip_time) { + highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kInfo, + "MIP-Timing: %11.2g - starting analytic centre calculation\n", + mipsolver.timer_.read()); + fflush(stdout); + } mipsolver.analysis_.mipTimerStart(kMipClockStartAnalyticCentreComputation); startAnalyticCenterComputation(tg); mipsolver.analysis_.mipTimerStop(kMipClockStartAnalyticCentreComputation); @@ -2045,8 +2054,8 @@ void HighsMipSolverData::evaluateRootNode() { mipsolver.analysis_.mipTimerStop(kMipClockSeparation); return; } - if (nseparounds >= 5 && !mipsolver.submip && !analyticCenterComputed && - compute_analytic_centre) { + if (nseparounds >= 5 && !mipsolver.submip && + compute_analytic_centre && !analyticCenterComputed) { if (checkLimits()) { mipsolver.analysis_.mipTimerStop(kMipClockSeparation); return; @@ -2141,7 +2150,7 @@ void HighsMipSolverData::evaluateRootNode() { rootlpsolobj = lp.getObjective(); lp.setIterationLimit(std::max(10000, int(10 * avgrootlpiters))); - if (!analyticCenterComputed && compute_analytic_centre) { + if (compute_analytic_centre && !analyticCenterComputed) { if (checkLimits()) return; mipsolver.analysis_.mipTimerStart(kMipClockFinishAnalyticCentreComputation); @@ -2263,7 +2272,7 @@ void HighsMipSolverData::evaluateRootNode() { if (lower_bound <= upper_limit) { if (!mipsolver.submip && mipsolver.options_mip_->mip_allow_restart && mipsolver.options_mip_->presolve != kHighsOffString) { - if (!analyticCenterComputed && compute_analytic_centre) { + if (compute_analytic_centre && !analyticCenterComputed) { mipsolver.analysis_.mipTimerStart( kMipClockFinishAnalyticCentreComputation); finishAnalyticCenterComputation(tg); diff --git a/src/mip/HighsMipSolverData.h b/src/mip/HighsMipSolverData.h index 38693bf361..8917b8dee2 100644 --- a/src/mip/HighsMipSolverData.h +++ b/src/mip/HighsMipSolverData.h @@ -84,6 +84,7 @@ struct HighsMipSolverData { bool cliquesExtracted; bool rowMatrixSet; bool analyticCenterComputed; + bool analyticCenterFailed; HighsModelStatus analyticCenterStatus; bool detectSymmetries; HighsInt numRestarts; @@ -170,6 +171,7 @@ struct HighsMipSolverData { cliquesExtracted(false), rowMatrixSet(false), analyticCenterComputed(false), + analyticCenterFailed(false), analyticCenterStatus(HighsModelStatus::kNotset), detectSymmetries(false), numRestarts(0), From 2eb63d3df1ec6b26adac5f75729dcd0550459da3 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 25 Nov 2024 16:26:30 +0000 Subject: [PATCH 02/40] Now not attempring AC calculation in sub-MIP if it failed (or was not attempted) in parent MIP --- src/lp_data/HighsOptions.h | 8 ++++++++ src/mip/HighsMipSolver.cpp | 11 +++++++---- src/mip/HighsMipSolverData.cpp | 13 +++++++------ src/mip/HighsMipSolverData.h | 2 ++ src/mip/HighsPrimalHeuristics.cpp | 6 +++++- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index 82424a0adc..3d6cc8d4f3 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -421,6 +421,7 @@ struct HighsOptionsStruct { HighsInt mip_pool_soft_limit; HighsInt mip_pscost_minreliable; HighsInt mip_min_cliquetable_entries_for_parallelism; + HighsInt mip_compute_analytic_centre; HighsInt mip_report_level; double mip_feasibility_tolerance; double mip_rel_gap; @@ -555,6 +556,7 @@ struct HighsOptionsStruct { mip_pool_soft_limit(0), mip_pscost_minreliable(0), mip_min_cliquetable_entries_for_parallelism(0), + mip_compute_analytic_centre(0), mip_report_level(0), mip_feasibility_tolerance(0.0), mip_rel_gap(0.0), @@ -1032,6 +1034,12 @@ class HighsOptions : public HighsOptionsStruct { kHighsIInf); records.push_back(record_int); + record_int = new OptionRecordInt( + "mip_compute_analytic_centre", + "Compute analytic centre for MIP: 0 => no; 1 => original (default) 2 => true", + advanced, &mip_compute_analytic_centre, 0, 1, 2); + records.push_back(record_int); + record_int = new OptionRecordInt("mip_report_level", "MIP solver reporting level", now_advanced, &mip_report_level, 0, 1, 2); diff --git a/src/mip/HighsMipSolver.cpp b/src/mip/HighsMipSolver.cpp index 2b5b93e6e6..ead83e320a 100644 --- a/src/mip/HighsMipSolver.cpp +++ b/src/mip/HighsMipSolver.cpp @@ -800,25 +800,28 @@ void HighsMipSolver::cleanupSolve() { " %.2f (presolve)\n" " %.2f (solve)\n" " %.2f (postsolve)\n" - " Max sub-MIP depth %d\n" " Nodes %llu\n" " Repair LPs %llu (%llu feasible; %llu iterations)\n" " LP iterations %llu (total)\n" " %llu (strong br.)\n" " %llu (separation)\n" - " %llu (heuristics)\n", + " %llu (heuristics)\n" + " AC failures %d\n" + " Max sub-MIP depth %d\n", timer_.read(timer_.total_clock), analysis_.mipTimerRead(kMipClockPresolve), analysis_.mipTimerRead(kMipClockSolve), analysis_.mipTimerRead(kMipClockPostsolve), - int(max_submip_level), (long long unsigned)mipdata_->num_nodes, + (long long unsigned)mipdata_->num_nodes, (long long unsigned)mipdata_->total_repair_lp, (long long unsigned)mipdata_->total_repair_lp_feasible, (long long unsigned)mipdata_->total_repair_lp_iterations, (long long unsigned)mipdata_->total_lp_iterations, (long long unsigned)mipdata_->sb_lp_iterations, (long long unsigned)mipdata_->sepa_lp_iterations, - (long long unsigned)mipdata_->heuristic_lp_iterations); + (long long unsigned)mipdata_->heuristic_lp_iterations, + int(mipdata_->num_analytic_centre_failures), + int(max_submip_level > 0 ? max_submip_level : 0)); analysis_.reportMipTimer(); diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index 3dd88092c9..3679b72c89 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -347,6 +347,7 @@ void HighsMipSolverData::finishAnalyticCenterComputation( } analyticCenterComputed = true; analyticCenterFailed = analyticCenterStatus == HighsModelStatus::kSolveError; + if (analyticCenterFailed) num_analytic_centre_failures++; if (analyticCenterStatus == HighsModelStatus::kOptimal) { HighsInt nfixed = 0; HighsInt nintfixed = 0; @@ -636,6 +637,7 @@ void HighsMipSolverData::init() { heuristic_lp_iterations_before_run = 0; sepa_lp_iterations_before_run = 0; sb_lp_iterations_before_run = 0; + num_analytic_centre_failures = 0; num_disp_lines = 0; numCliqueEntriesAfterPresolve = 0; numCliqueEntriesAfterFirstPresolve = 0; @@ -1857,8 +1859,6 @@ HighsLpRelaxation::Status HighsMipSolverData::evaluateRootLp() { } void HighsMipSolverData::evaluateRootNode() { - const bool compute_analytic_centre = true; - if (!compute_analytic_centre) printf("NOT COMPUTING ANALYTIC CENTRE!\n"); HighsInt maxSepaRounds = mipsolver.submip ? 5 : kHighsIInf; if (numRestarts == 0) maxSepaRounds = @@ -1871,7 +1871,8 @@ void HighsMipSolverData::evaluateRootNode() { startSymmetryDetection(tg, symData); mipsolver.analysis_.mipTimerStop(kMipClockStartSymmetryDetection); } - if (compute_analytic_centre && !analyticCenterComputed && !analyticCenterFailed) { + if (mipsolver.options_mip_->mip_compute_analytic_centre && + !analyticCenterComputed && !analyticCenterFailed) { if (mipsolver.analysis_.analyse_mip_time) { highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kInfo, "MIP-Timing: %11.2g - starting analytic centre calculation\n", @@ -2055,7 +2056,7 @@ void HighsMipSolverData::evaluateRootNode() { return; } if (nseparounds >= 5 && !mipsolver.submip && - compute_analytic_centre && !analyticCenterComputed) { + mipsolver.options_mip_->mip_compute_analytic_centre && !analyticCenterComputed) { if (checkLimits()) { mipsolver.analysis_.mipTimerStop(kMipClockSeparation); return; @@ -2150,7 +2151,7 @@ void HighsMipSolverData::evaluateRootNode() { rootlpsolobj = lp.getObjective(); lp.setIterationLimit(std::max(10000, int(10 * avgrootlpiters))); - if (compute_analytic_centre && !analyticCenterComputed) { + if (mipsolver.options_mip_->mip_compute_analytic_centre && !analyticCenterComputed) { if (checkLimits()) return; mipsolver.analysis_.mipTimerStart(kMipClockFinishAnalyticCentreComputation); @@ -2272,7 +2273,7 @@ void HighsMipSolverData::evaluateRootNode() { if (lower_bound <= upper_limit) { if (!mipsolver.submip && mipsolver.options_mip_->mip_allow_restart && mipsolver.options_mip_->presolve != kHighsOffString) { - if (compute_analytic_centre && !analyticCenterComputed) { + if (mipsolver.options_mip_->mip_compute_analytic_centre && !analyticCenterComputed) { mipsolver.analysis_.mipTimerStart( kMipClockFinishAnalyticCentreComputation); finishAnalyticCenterComputation(tg); diff --git a/src/mip/HighsMipSolverData.h b/src/mip/HighsMipSolverData.h index 8917b8dee2..6e101af638 100644 --- a/src/mip/HighsMipSolverData.h +++ b/src/mip/HighsMipSolverData.h @@ -120,6 +120,7 @@ struct HighsMipSolverData { HighsInt numintegercols; HighsInt maxTreeSizeLog2; + HighsInt num_analytic_centre_failures; HighsCDouble pruned_treeweight; double avgrootlpiters; double last_disptime; @@ -186,6 +187,7 @@ struct HighsMipSolverData { rootlpsolobj(-kHighsInf), numintegercols(0), maxTreeSizeLog2(0), + num_analytic_centre_failures(0), pruned_treeweight(0), avgrootlpiters(0.0), last_disptime(0.0), diff --git a/src/mip/HighsPrimalHeuristics.cpp b/src/mip/HighsPrimalHeuristics.cpp index 5908f4373c..6a9a2c1680 100644 --- a/src/mip/HighsPrimalHeuristics.cpp +++ b/src/mip/HighsPrimalHeuristics.cpp @@ -132,8 +132,12 @@ bool HighsPrimalHeuristics::solveSubMip( submipoptions.presolve = "on"; submipoptions.mip_detect_symmetry = false; submipoptions.mip_heuristic_effort = 0.8; - // setup solver and run it + // If the analytic centre calculation has failed for the parent MIP, + // then don't perform it for the sub-MIP + if (mipsolver.mipdata_->analyticCenterFailed) + submipoptions.mip_compute_analytic_centre = 0; + // setup solver and run it HighsSolution solution; solution.value_valid = false; solution.dual_valid = false; From b62b112d7e078ff57132ed621d0e241d83181673 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 25 Nov 2024 17:55:08 +0000 Subject: [PATCH 03/40] Introduce kkt_iter_limit --- src/ipm/ipx/conjugate_residuals.cc | 12 ++++++------ src/lp_data/HConst.h | 8 ++++++++ src/lp_data/HighsOptions.h | 5 ++++- src/mip/HighsMipSolverData.cpp | 23 +++++++++++++---------- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/ipm/ipx/conjugate_residuals.cc b/src/ipm/ipx/conjugate_residuals.cc index eb25be7118..87b6c1e6ca 100644 --- a/src/ipm/ipx/conjugate_residuals.cc +++ b/src/ipm/ipx/conjugate_residuals.cc @@ -13,6 +13,7 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, double tol, const double* resscale, Int maxiter, Vector& lhs) { const Int m = rhs.size(); + printf("ConjugateResiduals::Solve On entry, maxiter = %d\n", int(maxiter)); Vector residual(m); // rhs - C*lhs Vector step(m); // update to lhs Vector Cresidual(m); // C * residual @@ -26,10 +27,9 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, if (maxiter < 0) maxiter = m+100; - Int putative_large_iter = 100;//std::max(1000, maxiter/100); - maxiter = putative_large_iter; - Int large_iter = 0;//std::max(1000, maxiter/100); - Int report_frequency = 1;//100; + Int large_iter = std::max(1000, maxiter/100); + maxiter = std::min(10*large_iter, m+100); + Int report_frequency = 100; Int report_iter = 0; // Initialize residual, step and Cstep. @@ -65,8 +65,8 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, errflag_ = IPX_ERROR_cr_matrix_not_posdef; break; } - if (iter_ > large_iter && iter_ > report_iter + report_frequency) { - printf("ConjugateResiduals::Solve Iteration count (large = %d; max = %d) is %d\n", int(putative_large_iter), int(maxiter), int(iter_)); + if (iter_ >= std::max(large_iter, report_iter + report_frequency)) { + printf("ConjugateResiduals::Solve Iteration count (large = %d; max = %d) is %d\n", int(large_iter), int(maxiter), int(iter_)); report_iter = iter_; report_frequency *= 2; } diff --git a/src/lp_data/HConst.h b/src/lp_data/HConst.h index 5e6a9d67fc..d830f9a4fc 100644 --- a/src/lp_data/HConst.h +++ b/src/lp_data/HConst.h @@ -45,6 +45,14 @@ const double kExcessivelySmallCostValue = 1e-4; const bool kAllowDeveloperAssert = false; const bool kExtendInvertWhenAddingRows = false; +enum MipAnalyticCentreCalulation { + kMipAnalyticCentreCalulationMin = 0, + kMipAnalyticCentreCalulationNo = kMipAnalyticCentreCalulationMin, + kMipAnalyticCentreCalulationOriginal, + kMipAnalyticCentreCalulationTrue, + kMipAnalyticCentreCalulationMax = kMipAnalyticCentreCalulationTrue +}; + enum class HighsLogType { kInfo = 1, kDetailed, kVerbose, kWarning, kError }; enum SimplexScaleStrategy { diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index 3d6cc8d4f3..44886db306 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -1037,7 +1037,10 @@ class HighsOptions : public HighsOptionsStruct { record_int = new OptionRecordInt( "mip_compute_analytic_centre", "Compute analytic centre for MIP: 0 => no; 1 => original (default) 2 => true", - advanced, &mip_compute_analytic_centre, 0, 1, 2); + advanced, &mip_compute_analytic_centre, + kMipAnalyticCentreCalulationMin, + kMipAnalyticCentreCalulationOriginal, + kMipAnalyticCentreCalulationMax); records.push_back(record_int); record_int = diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index 3679b72c89..f7438d7358 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -302,24 +302,27 @@ void HighsMipSolverData::startAnalyticCenterComputation( taskGroup.spawn([&]() { // first check if the analytic centre computation should be cancelled, e.g. // due to early return in the root node evaluation + assert(mipsolver.options_mip_->mip_compute_analytic_centre >= kMipAnalyticCentreCalulationOriginal); Highs ipm; - ipm.setOptionValue("solver", "ipm"); - ipm.setOptionValue("run_crossover", kHighsOffString); + if (mipsolver.options_mip_->mip_compute_analytic_centre == kMipAnalyticCentreCalulationOriginal) { + // Original calculation is just IPM with crossover off + ipm.setOptionValue("solver", "ipm"); + ipm.setOptionValue("run_crossover", kHighsOffString); + } else { + // True calculation performs centring properly + ipm.setOptionValue("run_centring", true); + ipm.setOptionValue("ipm_optimality_tolerance", 1e-2); + } ipm.setOptionValue("presolve", "off"); ipm.setOptionValue("output_flag", false); - // ipm.setOptionValue("output_flag", !mipsolver.submip); + ipm.setOptionValue("output_flag", !mipsolver.submip); ipm.setOptionValue("ipm_iteration_limit", 200); HighsLp lpmodel(*mipsolver.model_); lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); ipm.passModel(std::move(lpmodel)); - // if (!mipsolver.submip) { - // const std::string file_name = mipsolver.model_->model_name_ + - // "_ipm.mps"; printf("Calling ipm.writeModel(%s)\n", - // file_name.c_str()); fflush(stdout); ipm.writeModel(file_name); - // } - printf("HighsMipSolverData::startAnalyticCenterComputation submip = %d analyticCenterFailed = %d; num_Col = %d; num_row = %d\n", - mipsolver.submip, analyticCenterFailed, int(ipm.getNumCol()), int(ipm.getNumRow())); + // if (!mipsolver.submip) ipm.writeModel(mipsolver.model_->model_name_ + "_ipm.mps"); + mipsolver.analysis_.mipTimerStart(kMipClockIpmSolveLp); ipm.run(); mipsolver.analysis_.mipTimerStop(kMipClockIpmSolveLp); From 0bb8cf08d2ebef2cdf908f9b83195c9474f11192 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 25 Nov 2024 21:14:47 +0000 Subject: [PATCH 04/40] Now to get data on max CG iterations --- src/ipm/IpxWrapper.cpp | 1 + src/ipm/ipx/conjugate_residuals.cc | 13 ++++++++----- src/ipm/ipx/control.cc | 4 +++- src/ipm/ipx/control.h | 1 + src/ipm/ipx/ipx_internal.h | 1 + src/ipm/ipx/ipx_parameters.h | 1 + src/ipm/ipx/lp_solver.cc | 1 + src/lp_data/HighsOptions.h | 7 +++++++ src/mip/HighsPrimalHeuristics.cpp | 3 +-- 9 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index eae2aff189..ed50953852 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -121,6 +121,7 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, parameters.time_limit = options.time_limit - timer.readRunHighsClock(); parameters.ipm_maxiter = options.ipm_iteration_limit - highs_info.ipm_iteration_count; + parameters.kkt_maxiter = options.kkt_iteration_limit; // Determine if crossover is to be run or not // // When doing analytic centring calculations, crossover must not be diff --git a/src/ipm/ipx/conjugate_residuals.cc b/src/ipm/ipx/conjugate_residuals.cc index 87b6c1e6ca..907f1a156b 100644 --- a/src/ipm/ipx/conjugate_residuals.cc +++ b/src/ipm/ipx/conjugate_residuals.cc @@ -13,7 +13,6 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, double tol, const double* resscale, Int maxiter, Vector& lhs) { const Int m = rhs.size(); - printf("ConjugateResiduals::Solve On entry, maxiter = %d\n", int(maxiter)); Vector residual(m); // rhs - C*lhs Vector step(m); // update to lhs Vector Cresidual(m); // C * residual @@ -27,10 +26,12 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, if (maxiter < 0) maxiter = m+100; + Int default_maxiter = maxiter; Int large_iter = std::max(1000, maxiter/100); maxiter = std::min(10*large_iter, m+100); Int report_frequency = 100; Int report_iter = 0; + printf("ConjugateResiduals::Solve Default maxiter = %d; using %d\n", int(default_maxiter), int(maxiter)); // Initialize residual, step and Cstep. if (Infnorm(lhs) == 0.0) { @@ -65,8 +66,8 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, errflag_ = IPX_ERROR_cr_matrix_not_posdef; break; } - if (iter_ >= std::max(large_iter, report_iter + report_frequency)) { - printf("ConjugateResiduals::Solve Iteration count (large = %d; max = %d) is %d\n", int(large_iter), int(maxiter), int(iter_)); + if (iter_ >= report_iter + report_frequency) { + printf("ConjugateResiduals::Solve Iteration count (large = %d; max = %d) is %d: time = %g\n", int(large_iter), int(maxiter), int(iter_), control_.Elapsed()); report_iter = iter_; report_frequency *= 2; } @@ -90,8 +91,10 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, cdot = cdotnew; iter_++; - if ((errflag_ = control_.InterruptCheck()) != 0) - break; + if ((errflag_ = control_.InterruptCheck()) != 0) { + printf("ConjugateResiduals::Solve control_.InterruptCheck()) != 0: time = %g\n", control_.Elapsed()); + break; + } } time_ = timer.Elapsed(); } diff --git a/src/ipm/ipx/control.cc b/src/ipm/ipx/control.cc index 9a149fab15..4b6a3c58e5 100644 --- a/src/ipm/ipx/control.cc +++ b/src/ipm/ipx/control.cc @@ -13,8 +13,10 @@ Control::Control() { Int Control::InterruptCheck(const Int ipm_iteration_count) const { HighsTaskExecutor::getThisWorkerDeque()->checkInterrupt(); if (parameters_.time_limit >= 0.0 && - parameters_.time_limit < timer_.Elapsed()) + parameters_.time_limit < timer_.Elapsed()) { + printf("Control::InterruptCheck Reached time limit of %g\n", parameters_.time_limit); return IPX_ERROR_time_interrupt; + } // The pointer callback_ should not be null, since that indicates // that it's not been set assert(callback_); diff --git a/src/ipm/ipx/control.h b/src/ipm/ipx/control.h index d322b0d383..99e96307f0 100644 --- a/src/ipm/ipx/control.h +++ b/src/ipm/ipx/control.h @@ -69,6 +69,7 @@ class Control { double ipm_drop_primal() const { return parameters_.ipm_drop_primal; } double ipm_drop_dual() const { return parameters_.ipm_drop_dual; } double kkt_tol() const { return parameters_.kkt_tol; } + ipxint kkt_maxiter() const { return parameters_.kkt_maxiter; } ipxint crash_basis() const { return parameters_.crash_basis; } double dependency_tol() const { return parameters_.dependency_tol; } double volume_tol() const { return parameters_.volume_tol; } diff --git a/src/ipm/ipx/ipx_internal.h b/src/ipm/ipx/ipx_internal.h index f51654b2ac..d9a2133099 100644 --- a/src/ipm/ipx/ipx_internal.h +++ b/src/ipm/ipx/ipx_internal.h @@ -32,6 +32,7 @@ struct Parameters : public ipx_parameters { ipm_drop_primal = 1e-9; ipm_drop_dual = 1e-9; kkt_tol = 0.3; + kkt_maxiter = -1; crash_basis = 1; dependency_tol = 1e-6; volume_tol = 2.0; diff --git a/src/ipm/ipx/ipx_parameters.h b/src/ipm/ipx/ipx_parameters.h index 2f9db89186..590f0b90f0 100644 --- a/src/ipm/ipx/ipx_parameters.h +++ b/src/ipm/ipx/ipx_parameters.h @@ -28,6 +28,7 @@ struct ipx_parameters { double ipm_drop_dual; /* Linear solver */ + ipxint kkt_maxiter; double kkt_tol; /* Basis construction in IPM */ diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index 6fb771587d..4ae972c92d 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -558,6 +558,7 @@ void LpSolver::RunMainIPM(IPM& ipm) { KKTSolverBasis kkt(control_, *basis_); Timer timer; ipm.maxiter(control_.ipm_maxiter()); + kkt.maxiter(control_.kkt_maxiter()); ipm.Driver(&kkt, iterate_.get(), &info_); info_.time_ipm2 = timer.Elapsed(); } diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index 44886db306..7976c0bd8c 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -333,6 +333,7 @@ struct HighsOptionsStruct { // Options for IPM solver HighsInt ipm_iteration_limit; + HighsInt kkt_iteration_limit; // Options for PDLP solver bool pdlp_native_termination; @@ -482,6 +483,7 @@ struct HighsOptionsStruct { output_flag(false), log_to_console(false), ipm_iteration_limit(0), + kkt_iteration_limit(0), pdlp_native_termination(false), pdlp_scaling(false), pdlp_iteration_limit(0), @@ -1082,6 +1084,11 @@ class HighsOptions : public HighsOptionsStruct { &ipm_iteration_limit, 0, kHighsIInf, kHighsIInf); records.push_back(record_int); + record_int = new OptionRecordInt( + "kkt_iteration_limit", "Iteration limit for PCG in IPX IPM solver: default is -1, so set internally", advanced, + &kkt_iteration_limit, -1, -1, kHighsIInf); + records.push_back(record_int); + record_bool = new OptionRecordBool( "pdlp_native_termination", "Use native termination for PDLP solver: Default = false", advanced, diff --git a/src/mip/HighsPrimalHeuristics.cpp b/src/mip/HighsPrimalHeuristics.cpp index 6a9a2c1680..3729858ec1 100644 --- a/src/mip/HighsPrimalHeuristics.cpp +++ b/src/mip/HighsPrimalHeuristics.cpp @@ -135,8 +135,7 @@ bool HighsPrimalHeuristics::solveSubMip( // If the analytic centre calculation has failed for the parent MIP, // then don't perform it for the sub-MIP - if (mipsolver.mipdata_->analyticCenterFailed) - submipoptions.mip_compute_analytic_centre = 0; + // if (mipsolver.mipdata_->analyticCenterFailed) submipoptions.mip_compute_analytic_centre = 0; // setup solver and run it HighsSolution solution; solution.value_valid = false; From 2e180e742e56b43b9a9ad1b09c2dc650a3f5b224 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 25 Nov 2024 22:08:04 +0000 Subject: [PATCH 05/40] changed iter to iterSum in IPX --- src/ipm/ipx/ipm.cc | 2 +- src/ipm/ipx/kkt_solver.cc | 3 ++- src/ipm/ipx/kkt_solver.h | 9 +++++++-- src/ipm/ipx/kkt_solver_basis.cc | 6 ++++-- src/ipm/ipx/kkt_solver_basis.h | 6 ++++-- src/ipm/ipx/kkt_solver_diag.cc | 6 ++++-- src/ipm/ipx/kkt_solver_diag.h | 6 ++++-- src/lp_data/HighsInfo.cpp | 1 + src/lp_data/HighsInfo.h | 6 ++++++ 9 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index 4a21364951..d8860f19fc 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -856,7 +856,7 @@ void IPM::PrintOutput() { control_.Debug() << " " << Fixed(step_primal_, 4, 2) << " " << Fixed(step_dual_, 4, 2) << " " << Format(kkt_->basis_changes(), 7) - << " " << Format(kkt_->iter(), 7); + << " " << Format(kkt_->iterSum(), 7); control_.Debug() << " " << Format(info_->dual_dropped, 7) << " " << Format(info_->primal_dropped, 7); diff --git a/src/ipm/ipx/kkt_solver.cc b/src/ipm/ipx/kkt_solver.cc index dde0967e52..1aaee27133 100644 --- a/src/ipm/ipx/kkt_solver.cc +++ b/src/ipm/ipx/kkt_solver.cc @@ -16,7 +16,8 @@ void KKTSolver::Solve(const Vector& a, const Vector& b, double tol, info->time_kkt_solve += timer.Elapsed(); } -Int KKTSolver::iter() const { return _iter(); } +Int KKTSolver::iterSum() const { return _iterSum(); } + //Int KKTSolver::iterMax() const { return _iterMax(); } Int KKTSolver::basis_changes() const { return _basis_changes(); } const Basis* KKTSolver::basis() const { return _basis(); } diff --git a/src/ipm/ipx/kkt_solver.h b/src/ipm/ipx/kkt_solver.h index 3889f1b97c..5de82440db 100644 --- a/src/ipm/ipx/kkt_solver.h +++ b/src/ipm/ipx/kkt_solver.h @@ -46,7 +46,12 @@ class KKTSolver { // If an iterative method is used, returns the # iterations in all Solve() // calls since the last call to Factorize(). A direct solver returns the # // iterative refinement steps. - Int iter() const; + Int iterSum() const; + + // If an iterative method is used, returns the max # iterations in + // all Solve() calls since the last call to Factorize(). A direct + // solver returns the max # iterative refinement steps. + // Int iterMax() const; // If a basis matrix is maintained, returns the # basis changes in the last // call to Factorize(). Otherwise returns 0. @@ -60,7 +65,7 @@ class KKTSolver { virtual void _Factorize(Iterate* iterate, Info* info) = 0; virtual void _Solve(const Vector& a, const Vector& b, double tol, Vector& x, Vector& y, Info* info) = 0; - virtual Int _iter() const = 0; + virtual Int _iterSum() const = 0; virtual Int _basis_changes() const { return 0; } virtual const Basis* _basis() const { return nullptr; } }; diff --git a/src/ipm/ipx/kkt_solver_basis.cc b/src/ipm/ipx/kkt_solver_basis.cc index dddb35ec8b..e335ec9dba 100644 --- a/src/ipm/ipx/kkt_solver_basis.cc +++ b/src/ipm/ipx/kkt_solver_basis.cc @@ -20,7 +20,8 @@ void KKTSolverBasis::_Factorize(Iterate* iterate, Info* info) { const Int n = model_.cols(); info->errflag = 0; factorized_ = false; - iter_ = 0; + iter_sum_ = 0; + iter_max_ = 0; basis_changes_ = 0; for (Int j = 0; j < n+m; j++) @@ -152,7 +153,8 @@ void KKTSolverBasis::_Solve(const Vector& a, const Vector& b, double tol, info->time_cr2_NNt += splitted_normal_matrix_.time_NNt(); info->time_cr2_B += splitted_normal_matrix_.time_B(); info->time_cr2_Bt += splitted_normal_matrix_.time_Bt(); - iter_ += cr.iter(); + iter_sum_ += cr.iter(); + iter_max_ = std::max(cr.iter(), iter_max_); // Permute back solution to normal equations. for (Int k = 0; k < m; k++) diff --git a/src/ipm/ipx/kkt_solver_basis.h b/src/ipm/ipx/kkt_solver_basis.h index db7e1af131..da637324bd 100644 --- a/src/ipm/ipx/kkt_solver_basis.h +++ b/src/ipm/ipx/kkt_solver_basis.h @@ -33,7 +33,8 @@ class KKTSolverBasis : public KKTSolver { void _Factorize(Iterate* iterate, Info* info) override; void _Solve(const Vector& a, const Vector& b, double tol, Vector& x, Vector& y, Info* info) override; - Int _iter() const override { return iter_; } + Int _iterSum() const override { return iter_sum_; } + // Int _iterMax() const override { return iter_max_; } Int _basis_changes() const override { return basis_changes_; } const Basis* _basis() const override { return &basis_; } @@ -57,7 +58,8 @@ class KKTSolverBasis : public KKTSolver { Vector colscale_; // interior point column scaling factors bool factorized_{false}; // preconditioner prepared? Int maxiter_{-1}; - Int iter_{0}; + Int iter_sum_{0}; + Int iter_max_{0}; Int basis_changes_{0}; }; diff --git a/src/ipm/ipx/kkt_solver_diag.cc b/src/ipm/ipx/kkt_solver_diag.cc index 382c9d06cc..4ff530cb37 100644 --- a/src/ipm/ipx/kkt_solver_diag.cc +++ b/src/ipm/ipx/kkt_solver_diag.cc @@ -18,7 +18,8 @@ KKTSolverDiag::KKTSolverDiag(const Control& control, const Model& model) : void KKTSolverDiag::_Factorize(Iterate* pt, Info* info) { const Int m = model_.rows(); const Int n = model_.cols(); - iter_ = 0; + iter_sum_ = 0; + iter_max_ = 0; factorized_ = false; if (pt) { @@ -102,7 +103,8 @@ void KKTSolverDiag::_Solve(const Vector& a, const Vector& b, double tol, info->time_cr1 += cr.time(); info->time_cr1_AAt += normal_matrix_.time(); info->time_cr1_pre += precond_.time(); - iter_ += cr.iter(); + iter_sum_ += cr.iter(); + iter_max_ = std::max(cr.iter(), iter_max_); // Recover solution to KKT system. for (Int i = 0; i < m; i++) diff --git a/src/ipm/ipx/kkt_solver_diag.h b/src/ipm/ipx/kkt_solver_diag.h index 31a2b708ba..66029e0a6a 100644 --- a/src/ipm/ipx/kkt_solver_diag.h +++ b/src/ipm/ipx/kkt_solver_diag.h @@ -29,7 +29,8 @@ class KKTSolverDiag : public KKTSolver { void _Factorize(Iterate* iterate, Info* info) override; void _Solve(const Vector& a, const Vector& b, double tol, Vector& x, Vector& y, Info* info) override; - Int _iter() const override { return iter_; }; + Int _iterSum() const override { return iter_sum_; }; + // Int _iterMax() const override { return iter_max_; }; const Control& control_; const Model& model_; @@ -40,7 +41,8 @@ class KKTSolverDiag : public KKTSolver { Vector resscale_; // residual scaling factors for CR termination test bool factorized_{false}; // KKT matrix factorized? Int maxiter_{-1}; - Int iter_{0}; // # CR iterations since last Factorize() + Int iter_sum_{0}; + Int iter_max_{0}; }; } // namespace ipx diff --git a/src/lp_data/HighsInfo.cpp b/src/lp_data/HighsInfo.cpp index 2792f64550..987c18cc70 100644 --- a/src/lp_data/HighsInfo.cpp +++ b/src/lp_data/HighsInfo.cpp @@ -41,6 +41,7 @@ void HighsInfo::invalidate() { max_complementarity_violation = kHighsIllegalComplementarityViolation; sum_complementarity_violations = kHighsIllegalComplementarityViolation; primal_dual_integral = -kHighsInf; + max_cr_iteration_count = -1; } static std::string infoEntryTypeToString(const HighsInfoType type) { diff --git a/src/lp_data/HighsInfo.h b/src/lp_data/HighsInfo.h index 3292b8281e..294b4350ed 100644 --- a/src/lp_data/HighsInfo.h +++ b/src/lp_data/HighsInfo.h @@ -116,6 +116,7 @@ struct HighsInfoStruct { double max_complementarity_violation; double sum_complementarity_violations; double primal_dual_integral; + HighsInt max_cr_iteration_count; }; class HighsInfo : public HighsInfoStruct { @@ -279,6 +280,11 @@ class HighsInfo : public HighsInfoStruct { new InfoRecordDouble("primal_dual_integral", "Primal-dual integral", advanced, &primal_dual_integral, 0); records.push_back(record_double); + + record_int = new InfoRecordInt("max_cr_iteration_count", + "Maximum number of CG iterations in IPX", advanced, + &max_cr_iteration_count, -1); + records.push_back(record_int); } public: From ddd52b30beb06f3db57f4ccd6a6f6dfa1484d258 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 25 Nov 2024 22:53:03 +0000 Subject: [PATCH 06/40] Ready to do some testing to identify limit on KKT iterations --- src/ipm/IpxWrapper.cpp | 6 ++++++ src/ipm/ipx/conjugate_residuals.cc | 13 ------------- src/ipm/ipx/info.cc | 2 ++ src/ipm/ipx/ipx_info.h | 2 ++ src/ipm/ipx/kkt_solver.cc | 2 +- src/ipm/ipx/kkt_solver.h | 6 +++--- src/ipm/ipx/kkt_solver_basis.cc | 2 +- src/ipm/ipx/kkt_solver_basis.h | 2 +- src/ipm/ipx/kkt_solver_diag.cc | 2 +- src/ipm/ipx/kkt_solver_diag.h | 2 +- src/lp_data/HighsInfo.cpp | 3 ++- src/lp_data/HighsInfo.h | 16 +++++++++++---- src/lp_data/HighsOptions.h | 19 +++++++++--------- src/mip/HighsMipSolver.cpp | 6 +++--- src/mip/HighsMipSolverData.cpp | 31 +++++++++++++++++++++--------- src/mip/HighsMipSolverData.h | 4 ++-- src/mip/HighsPrimalHeuristics.cpp | 3 ++- 17 files changed, 71 insertions(+), 50 deletions(-) diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index ed50953852..db48e9c7b7 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -184,6 +184,8 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, if (report_solve_data) reportSolveData(options.log_options, ipx_info); highs_info.ipm_iteration_count += (HighsInt)ipx_info.iter; highs_info.crossover_iteration_count += (HighsInt)ipx_info.updates_crossover; + highs_info.max_cr_iteration_count1 = ipx_info.kkt_iter_max1; + highs_info.max_cr_iteration_count2 = ipx_info.kkt_iter_max2; // If not solved... if (solve_status != IPX_STATUS_solved) { @@ -961,6 +963,10 @@ void reportSolveData(const HighsLogOptions& log_options, (int)ipx_info.kktiter1); highsLogDev(log_options, HighsLogType::kInfo, " KKT iter 2 = %d\n", (int)ipx_info.kktiter2); + highsLogDev(log_options, HighsLogType::kInfo, " KKT iter max 1 = %d\n", + (int)ipx_info.kkt_iter_max1); + highsLogDev(log_options, HighsLogType::kInfo, " KKT iter max 2 = %d\n", + (int)ipx_info.kkt_iter_max2); highsLogDev(log_options, HighsLogType::kInfo, " Basis repairs = %d\n", (int)ipx_info.basis_repairs); highsLogDev(log_options, HighsLogType::kInfo, " Updates start = %d\n", diff --git a/src/ipm/ipx/conjugate_residuals.cc b/src/ipm/ipx/conjugate_residuals.cc index 907f1a156b..cb16878743 100644 --- a/src/ipm/ipx/conjugate_residuals.cc +++ b/src/ipm/ipx/conjugate_residuals.cc @@ -26,13 +26,6 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, if (maxiter < 0) maxiter = m+100; - Int default_maxiter = maxiter; - Int large_iter = std::max(1000, maxiter/100); - maxiter = std::min(10*large_iter, m+100); - Int report_frequency = 100; - Int report_iter = 0; - printf("ConjugateResiduals::Solve Default maxiter = %d; using %d\n", int(default_maxiter), int(maxiter)); - // Initialize residual, step and Cstep. if (Infnorm(lhs) == 0.0) { residual = rhs; // saves a matrix-vector op @@ -66,12 +59,6 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, errflag_ = IPX_ERROR_cr_matrix_not_posdef; break; } - if (iter_ >= report_iter + report_frequency) { - printf("ConjugateResiduals::Solve Iteration count (large = %d; max = %d) is %d: time = %g\n", int(large_iter), int(maxiter), int(iter_), control_.Elapsed()); - report_iter = iter_; - report_frequency *= 2; - } - // Update lhs, residual and Cresidual. const double denom = Dot(Cstep,Cstep); const double alpha = cdot/denom; diff --git a/src/ipm/ipx/info.cc b/src/ipm/ipx/info.cc index fa09ab6cff..a64dea6a58 100644 --- a/src/ipm/ipx/info.cc +++ b/src/ipm/ipx/info.cc @@ -53,6 +53,8 @@ std::ostream& operator<<(std::ostream& os, const Info& info) { dump(os, "iter", info.iter); dump(os, "kktiter1", info.kktiter1); dump(os, "kktiter2", info.kktiter2); + dump(os, "kkt_iter_max1", info.kkt_iter_max1); + dump(os, "kkt_iter_max2", info.kkt_iter_max2); dump(os, "basis_repairs", info.basis_repairs); dump(os, "updates_start", info.updates_start); dump(os, "updates_ipm", info.updates_ipm); diff --git a/src/ipm/ipx/ipx_info.h b/src/ipm/ipx/ipx_info.h index ed43d6718e..03cda96090 100644 --- a/src/ipm/ipx/ipx_info.h +++ b/src/ipm/ipx/ipx_info.h @@ -58,6 +58,8 @@ struct ipx_info { ipxint iter; /* # interior point iterations */ ipxint kktiter1; /* # linear solver iterations before switch */ ipxint kktiter2; /* # linear solver iterations after switch */ + ipxint kkt_iter_max1; /* # max linear solver iterations before switch */ + ipxint kkt_iter_max2; /* # max linear solver iterations after switch */ ipxint basis_repairs; /* # basis repairs after crash, < 0 discarded */ ipxint updates_start; /* # basis updates for starting basis */ ipxint updates_ipm; /* # basis updates in IPM */ diff --git a/src/ipm/ipx/kkt_solver.cc b/src/ipm/ipx/kkt_solver.cc index 1aaee27133..a828fcb0a3 100644 --- a/src/ipm/ipx/kkt_solver.cc +++ b/src/ipm/ipx/kkt_solver.cc @@ -17,7 +17,7 @@ void KKTSolver::Solve(const Vector& a, const Vector& b, double tol, } Int KKTSolver::iterSum() const { return _iterSum(); } - //Int KKTSolver::iterMax() const { return _iterMax(); } +Int KKTSolver::iterMax() const { return _iterMax(); } Int KKTSolver::basis_changes() const { return _basis_changes(); } const Basis* KKTSolver::basis() const { return _basis(); } diff --git a/src/ipm/ipx/kkt_solver.h b/src/ipm/ipx/kkt_solver.h index 5de82440db..e5ab15bc22 100644 --- a/src/ipm/ipx/kkt_solver.h +++ b/src/ipm/ipx/kkt_solver.h @@ -49,9 +49,8 @@ class KKTSolver { Int iterSum() const; // If an iterative method is used, returns the max # iterations in - // all Solve() calls since the last call to Factorize(). A direct - // solver returns the max # iterative refinement steps. - // Int iterMax() const; + // _all_ Solve() calls. + Int iterMax() const; // If a basis matrix is maintained, returns the # basis changes in the last // call to Factorize(). Otherwise returns 0. @@ -66,6 +65,7 @@ class KKTSolver { virtual void _Solve(const Vector& a, const Vector& b, double tol, Vector& x, Vector& y, Info* info) = 0; virtual Int _iterSum() const = 0; + virtual Int _iterMax() const = 0; virtual Int _basis_changes() const { return 0; } virtual const Basis* _basis() const { return nullptr; } }; diff --git a/src/ipm/ipx/kkt_solver_basis.cc b/src/ipm/ipx/kkt_solver_basis.cc index e335ec9dba..46d28e2766 100644 --- a/src/ipm/ipx/kkt_solver_basis.cc +++ b/src/ipm/ipx/kkt_solver_basis.cc @@ -21,7 +21,6 @@ void KKTSolverBasis::_Factorize(Iterate* iterate, Info* info) { info->errflag = 0; factorized_ = false; iter_sum_ = 0; - iter_max_ = 0; basis_changes_ = 0; for (Int j = 0; j < n+m; j++) @@ -149,6 +148,7 @@ void KKTSolverBasis::_Solve(const Vector& a, const Vector& b, double tol, cr.Solve(splitted_normal_matrix_, work, tol, nullptr, maxiter_, lhs); info->errflag = cr.errflag(); info->kktiter2 += cr.iter(); + info->kkt_iter_max2 = std::max(cr.iter(), info->kkt_iter_max2); info->time_cr2 += cr.time(); info->time_cr2_NNt += splitted_normal_matrix_.time_NNt(); info->time_cr2_B += splitted_normal_matrix_.time_B(); diff --git a/src/ipm/ipx/kkt_solver_basis.h b/src/ipm/ipx/kkt_solver_basis.h index da637324bd..b46a89e5c2 100644 --- a/src/ipm/ipx/kkt_solver_basis.h +++ b/src/ipm/ipx/kkt_solver_basis.h @@ -34,7 +34,7 @@ class KKTSolverBasis : public KKTSolver { void _Solve(const Vector& a, const Vector& b, double tol, Vector& x, Vector& y, Info* info) override; Int _iterSum() const override { return iter_sum_; } - // Int _iterMax() const override { return iter_max_; } + Int _iterMax() const override { return iter_max_; } Int _basis_changes() const override { return basis_changes_; } const Basis* _basis() const override { return &basis_; } diff --git a/src/ipm/ipx/kkt_solver_diag.cc b/src/ipm/ipx/kkt_solver_diag.cc index 4ff530cb37..9e66ad6848 100644 --- a/src/ipm/ipx/kkt_solver_diag.cc +++ b/src/ipm/ipx/kkt_solver_diag.cc @@ -19,7 +19,6 @@ void KKTSolverDiag::_Factorize(Iterate* pt, Info* info) { const Int m = model_.rows(); const Int n = model_.cols(); iter_sum_ = 0; - iter_max_ = 0; factorized_ = false; if (pt) { @@ -100,6 +99,7 @@ void KKTSolverDiag::_Solve(const Vector& a, const Vector& b, double tol, cr.Solve(normal_matrix_, precond_, rhs, tol, &resscale_[0], maxiter_, y); info->errflag = cr.errflag(); info->kktiter1 += cr.iter(); + info->kkt_iter_max1 = std::max(cr.iter(), info->kkt_iter_max1); info->time_cr1 += cr.time(); info->time_cr1_AAt += normal_matrix_.time(); info->time_cr1_pre += precond_.time(); diff --git a/src/ipm/ipx/kkt_solver_diag.h b/src/ipm/ipx/kkt_solver_diag.h index 66029e0a6a..aaa18cfb85 100644 --- a/src/ipm/ipx/kkt_solver_diag.h +++ b/src/ipm/ipx/kkt_solver_diag.h @@ -30,7 +30,7 @@ class KKTSolverDiag : public KKTSolver { void _Solve(const Vector& a, const Vector& b, double tol, Vector& x, Vector& y, Info* info) override; Int _iterSum() const override { return iter_sum_; }; - // Int _iterMax() const override { return iter_max_; }; + Int _iterMax() const override { return iter_max_; }; const Control& control_; const Model& model_; diff --git a/src/lp_data/HighsInfo.cpp b/src/lp_data/HighsInfo.cpp index 987c18cc70..bbb07eb170 100644 --- a/src/lp_data/HighsInfo.cpp +++ b/src/lp_data/HighsInfo.cpp @@ -41,7 +41,8 @@ void HighsInfo::invalidate() { max_complementarity_violation = kHighsIllegalComplementarityViolation; sum_complementarity_violations = kHighsIllegalComplementarityViolation; primal_dual_integral = -kHighsInf; - max_cr_iteration_count = -1; + max_cr_iteration_count1 = -1; + max_cr_iteration_count2 = -1; } static std::string infoEntryTypeToString(const HighsInfoType type) { diff --git a/src/lp_data/HighsInfo.h b/src/lp_data/HighsInfo.h index 294b4350ed..70b56c20ef 100644 --- a/src/lp_data/HighsInfo.h +++ b/src/lp_data/HighsInfo.h @@ -116,7 +116,8 @@ struct HighsInfoStruct { double max_complementarity_violation; double sum_complementarity_violations; double primal_dual_integral; - HighsInt max_cr_iteration_count; + HighsInt max_cr_iteration_count1; + HighsInt max_cr_iteration_count2; }; class HighsInfo : public HighsInfoStruct { @@ -281,9 +282,16 @@ class HighsInfo : public HighsInfoStruct { advanced, &primal_dual_integral, 0); records.push_back(record_double); - record_int = new InfoRecordInt("max_cr_iteration_count", - "Maximum number of CG iterations in IPX", advanced, - &max_cr_iteration_count, -1); + record_int = + new InfoRecordInt("max_cr_iteration_count1", + "Maximum number of diag CR iterations in IPX", + advanced, &max_cr_iteration_count1, -1); + records.push_back(record_int); + + record_int = + new InfoRecordInt("max_cr_iteration_count2", + "Maximum number of basic CR iterations in IPX", + advanced, &max_cr_iteration_count2, -1); records.push_back(record_int); } diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index 7976c0bd8c..73c261a889 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -558,7 +558,7 @@ struct HighsOptionsStruct { mip_pool_soft_limit(0), mip_pscost_minreliable(0), mip_min_cliquetable_entries_for_parallelism(0), - mip_compute_analytic_centre(0), + mip_compute_analytic_centre(0), mip_report_level(0), mip_feasibility_tolerance(0.0), mip_rel_gap(0.0), @@ -1038,11 +1038,10 @@ class HighsOptions : public HighsOptionsStruct { record_int = new OptionRecordInt( "mip_compute_analytic_centre", - "Compute analytic centre for MIP: 0 => no; 1 => original (default) 2 => true", - advanced, &mip_compute_analytic_centre, - kMipAnalyticCentreCalulationMin, - kMipAnalyticCentreCalulationOriginal, - kMipAnalyticCentreCalulationMax); + "Compute analytic centre for MIP: 0 => no; 1 => original (default) 2 " + "=> true", + advanced, &mip_compute_analytic_centre, kMipAnalyticCentreCalulationMin, + kMipAnalyticCentreCalulationOriginal, kMipAnalyticCentreCalulationMax); records.push_back(record_int); record_int = @@ -1084,9 +1083,11 @@ class HighsOptions : public HighsOptionsStruct { &ipm_iteration_limit, 0, kHighsIInf, kHighsIInf); records.push_back(record_int); - record_int = new OptionRecordInt( - "kkt_iteration_limit", "Iteration limit for PCG in IPX IPM solver: default is -1, so set internally", advanced, - &kkt_iteration_limit, -1, -1, kHighsIInf); + record_int = + new OptionRecordInt("kkt_iteration_limit", + "Iteration limit for PCG in IPX IPM solver: " + "default is -1, so set internally", + advanced, &kkt_iteration_limit, -1, -1, kHighsIInf); records.push_back(record_int); record_bool = new OptionRecordBool( diff --git a/src/mip/HighsMipSolver.cpp b/src/mip/HighsMipSolver.cpp index ead83e320a..66520a4470 100644 --- a/src/mip/HighsMipSolver.cpp +++ b/src/mip/HighsMipSolver.cpp @@ -806,13 +806,13 @@ void HighsMipSolver::cleanupSolve() { " %llu (strong br.)\n" " %llu (separation)\n" " %llu (heuristics)\n" - " AC failures %d\n" + " AC failures %d\n" " Max sub-MIP depth %d\n", timer_.read(timer_.total_clock), analysis_.mipTimerRead(kMipClockPresolve), analysis_.mipTimerRead(kMipClockSolve), analysis_.mipTimerRead(kMipClockPostsolve), - (long long unsigned)mipdata_->num_nodes, + (long long unsigned)mipdata_->num_nodes, (long long unsigned)mipdata_->total_repair_lp, (long long unsigned)mipdata_->total_repair_lp_feasible, (long long unsigned)mipdata_->total_repair_lp_iterations, @@ -820,7 +820,7 @@ void HighsMipSolver::cleanupSolve() { (long long unsigned)mipdata_->sb_lp_iterations, (long long unsigned)mipdata_->sepa_lp_iterations, (long long unsigned)mipdata_->heuristic_lp_iterations, - int(mipdata_->num_analytic_centre_failures), + int(mipdata_->num_analytic_centre_failures), int(max_submip_level > 0 ? max_submip_level : 0)); analysis_.reportMipTimer(); diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index f7438d7358..5fe9bff4b9 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -302,9 +302,11 @@ void HighsMipSolverData::startAnalyticCenterComputation( taskGroup.spawn([&]() { // first check if the analytic centre computation should be cancelled, e.g. // due to early return in the root node evaluation - assert(mipsolver.options_mip_->mip_compute_analytic_centre >= kMipAnalyticCentreCalulationOriginal); + assert(mipsolver.options_mip_->mip_compute_analytic_centre >= + kMipAnalyticCentreCalulationOriginal); Highs ipm; - if (mipsolver.options_mip_->mip_compute_analytic_centre == kMipAnalyticCentreCalulationOriginal) { + if (mipsolver.options_mip_->mip_compute_analytic_centre == + kMipAnalyticCentreCalulationOriginal) { // Original calculation is just IPM with crossover off ipm.setOptionValue("solver", "ipm"); ipm.setOptionValue("run_crossover", kHighsOffString); @@ -321,11 +323,18 @@ void HighsMipSolverData::startAnalyticCenterComputation( lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); ipm.passModel(std::move(lpmodel)); - // if (!mipsolver.submip) ipm.writeModel(mipsolver.model_->model_name_ + "_ipm.mps"); + // if (!mipsolver.submip) ipm.writeModel(mipsolver.model_->model_name_ + + // "_ipm.mps"); mipsolver.analysis_.mipTimerStart(kMipClockIpmSolveLp); ipm.run(); mipsolver.analysis_.mipTimerStop(kMipClockIpmSolveLp); + if (!mipsolver.submip) { + printf("grepAC: model; num_row; max CR counts are, %s, %d, %d, %d\n", + mipsolver.model_->model_name_.c_str(), int(ipm.getNumRow()), + int(ipm.getInfo().max_cr_iteration_count1), + int(ipm.getInfo().max_cr_iteration_count2)); + } const std::vector& sol = ipm.getSolution().col_value; if (HighsInt(sol.size()) != mipsolver.numCol()) return; analyticCenterStatus = ipm.getModelStatus(); @@ -1877,9 +1886,10 @@ void HighsMipSolverData::evaluateRootNode() { if (mipsolver.options_mip_->mip_compute_analytic_centre && !analyticCenterComputed && !analyticCenterFailed) { if (mipsolver.analysis_.analyse_mip_time) { - highsLogUser(mipsolver.options_mip_->log_options, HighsLogType::kInfo, - "MIP-Timing: %11.2g - starting analytic centre calculation\n", - mipsolver.timer_.read()); + highsLogUser( + mipsolver.options_mip_->log_options, HighsLogType::kInfo, + "MIP-Timing: %11.2g - starting analytic centre calculation\n", + mipsolver.timer_.read()); fflush(stdout); } mipsolver.analysis_.mipTimerStart(kMipClockStartAnalyticCentreComputation); @@ -2059,7 +2069,8 @@ void HighsMipSolverData::evaluateRootNode() { return; } if (nseparounds >= 5 && !mipsolver.submip && - mipsolver.options_mip_->mip_compute_analytic_centre && !analyticCenterComputed) { + mipsolver.options_mip_->mip_compute_analytic_centre && + !analyticCenterComputed) { if (checkLimits()) { mipsolver.analysis_.mipTimerStop(kMipClockSeparation); return; @@ -2154,7 +2165,8 @@ void HighsMipSolverData::evaluateRootNode() { rootlpsolobj = lp.getObjective(); lp.setIterationLimit(std::max(10000, int(10 * avgrootlpiters))); - if (mipsolver.options_mip_->mip_compute_analytic_centre && !analyticCenterComputed) { + if (mipsolver.options_mip_->mip_compute_analytic_centre && + !analyticCenterComputed) { if (checkLimits()) return; mipsolver.analysis_.mipTimerStart(kMipClockFinishAnalyticCentreComputation); @@ -2276,7 +2288,8 @@ void HighsMipSolverData::evaluateRootNode() { if (lower_bound <= upper_limit) { if (!mipsolver.submip && mipsolver.options_mip_->mip_allow_restart && mipsolver.options_mip_->presolve != kHighsOffString) { - if (mipsolver.options_mip_->mip_compute_analytic_centre && !analyticCenterComputed) { + if (mipsolver.options_mip_->mip_compute_analytic_centre && + !analyticCenterComputed) { mipsolver.analysis_.mipTimerStart( kMipClockFinishAnalyticCentreComputation); finishAnalyticCenterComputation(tg); diff --git a/src/mip/HighsMipSolverData.h b/src/mip/HighsMipSolverData.h index 6e101af638..915d7c9761 100644 --- a/src/mip/HighsMipSolverData.h +++ b/src/mip/HighsMipSolverData.h @@ -172,7 +172,7 @@ struct HighsMipSolverData { cliquesExtracted(false), rowMatrixSet(false), analyticCenterComputed(false), - analyticCenterFailed(false), + analyticCenterFailed(false), analyticCenterStatus(HighsModelStatus::kNotset), detectSymmetries(false), numRestarts(0), @@ -187,7 +187,7 @@ struct HighsMipSolverData { rootlpsolobj(-kHighsInf), numintegercols(0), maxTreeSizeLog2(0), - num_analytic_centre_failures(0), + num_analytic_centre_failures(0), pruned_treeweight(0), avgrootlpiters(0.0), last_disptime(0.0), diff --git a/src/mip/HighsPrimalHeuristics.cpp b/src/mip/HighsPrimalHeuristics.cpp index 3729858ec1..d3a90ef8b7 100644 --- a/src/mip/HighsPrimalHeuristics.cpp +++ b/src/mip/HighsPrimalHeuristics.cpp @@ -135,7 +135,8 @@ bool HighsPrimalHeuristics::solveSubMip( // If the analytic centre calculation has failed for the parent MIP, // then don't perform it for the sub-MIP - // if (mipsolver.mipdata_->analyticCenterFailed) submipoptions.mip_compute_analytic_centre = 0; + // if (mipsolver.mipdata_->analyticCenterFailed) + // submipoptions.mip_compute_analytic_centre = 0; // setup solver and run it HighsSolution solution; solution.value_valid = false; From f3ff0b4da0aceeae74dfb606d0a5b3aa83a3f8b8 Mon Sep 17 00:00:00 2001 From: jajhall Date: Tue, 26 Nov 2024 15:53:35 +0000 Subject: [PATCH 07/40] Now to test AC, gathering more data --- src/ipm/ipx/conjugate_residuals.cc | 31 +++++++++++++++++-------- src/mip/HighsMipSolver.cpp | 18 +++++++++++---- src/mip/HighsMipSolverData.cpp | 36 +++++++++++++++++++++++------- src/mip/HighsMipSolverData.h | 10 +++++++-- src/mip/HighsPrimalHeuristics.cpp | 1 + 5 files changed, 73 insertions(+), 23 deletions(-) diff --git a/src/ipm/ipx/conjugate_residuals.cc b/src/ipm/ipx/conjugate_residuals.cc index cb16878743..f7345def7c 100644 --- a/src/ipm/ipx/conjugate_residuals.cc +++ b/src/ipm/ipx/conjugate_residuals.cc @@ -9,6 +9,8 @@ namespace ipx { ConjugateResiduals::ConjugateResiduals(const Control& control) : control_(control) {} +const bool cr_logging = false; + void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, double tol, const double* resscale, Int maxiter, Vector& lhs) { @@ -23,9 +25,12 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, errflag_ = 0; iter_ = 0; time_ = 0.0; + Int use_maxiter = maxiter; if (maxiter < 0) - maxiter = m+100; + use_maxiter = m+100; + if (cr_logging) printf("\nCR(C) maxiter = (entry = %d, use = %d)\n", int(maxiter), int(use_maxiter)); fflush(stdout); + maxiter = use_maxiter; // Initialize residual, step and Cstep. if (Infnorm(lhs) == 0.0) { residual = rhs; // saves a matrix-vector op @@ -37,9 +42,10 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, step = residual; Cstep = Cresidual; + double resnorm; while (true) { // Termination check. - double resnorm = 0.0; + resnorm = 0.0; if (resscale) for (Int i = 0; i < m; i++) resnorm = std::max(resnorm, std::abs(resscale[i]*residual[i])); @@ -48,6 +54,7 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, if (resnorm <= tol) break; if (iter_ == maxiter) { + if (cr_logging) printf("CR(C) reached maxiter!\n\n"); fflush(stdout); control_.Debug(3) << " CR method not converged in " << maxiter << " iterations." << " residual = " << sci2(resnorm) << ',' @@ -78,11 +85,10 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, cdot = cdotnew; iter_++; - if ((errflag_ = control_.InterruptCheck()) != 0) { - printf("ConjugateResiduals::Solve control_.InterruptCheck()) != 0: time = %g\n", control_.Elapsed()); - break; - } + if ((errflag_ = control_.InterruptCheck()) != 0) break; } + if (errflag_ != IPX_ERROR_cr_iter_limit) + if (cr_logging) printf("CR(C) iter = %d; resnorm = %g <= %g = tol? %s\n\n", int(iter_), resnorm, tol, resnorm <= tol ? "T" : "F"); fflush(stdout); time_ = timer.Elapsed(); } @@ -110,9 +116,12 @@ void ConjugateResiduals::Solve(LinearOperator& C, LinearOperator& P, errflag_ = 0; iter_ = 0; time_ = 0.0; + Int use_maxiter = maxiter; if (maxiter < 0) - maxiter = m+100; + use_maxiter = m+100; + if (cr_logging) printf("\nCR(C,P) maxiter = (entry = %d, use = %d)\n", int(maxiter), int(use_maxiter)); fflush(stdout); + maxiter = use_maxiter; // Initialize residual, sresidual, step and Cstep. if (Infnorm(lhs) == 0.0) { residual = rhs; // saves a matrix-vector op @@ -125,9 +134,10 @@ void ConjugateResiduals::Solve(LinearOperator& C, LinearOperator& P, step = sresidual; Cstep = Csresidual; + double resnorm; while (true) { // Termination check. - double resnorm = 0.0; + resnorm = 0.0; if (resscale) for (Int i = 0; i < m; i++) resnorm = std::max(resnorm, std::abs(resscale[i]*residual[i])); @@ -136,7 +146,8 @@ void ConjugateResiduals::Solve(LinearOperator& C, LinearOperator& P, if (resnorm <= tol) break; if (iter_ == maxiter) { - control_.Debug(3) + if (cr_logging) printf("CR(C,P) reached maxiter!\n\n"); fflush(stdout); + control_.Debug(3) << " PCR method not converged in " << maxiter << " iterations." << " residual = " << sci2(resnorm) << ',' << " tolerance = " << sci2(tol) << '\n'; @@ -208,6 +219,8 @@ void ConjugateResiduals::Solve(LinearOperator& C, LinearOperator& P, if ((errflag_ = control_.InterruptCheck()) != 0) break; } + if (errflag_ != IPX_ERROR_cr_iter_limit) + if (cr_logging) printf("CR(C,P) iter = %d; resnorm = %g <= %g = tol? %s\n\n", int(iter_), resnorm, tol, resnorm <= tol ? "T" : "F"); fflush(stdout); time_ = timer.Elapsed(); } diff --git a/src/mip/HighsMipSolver.cpp b/src/mip/HighsMipSolver.cpp index 66520a4470..6585c0ddb5 100644 --- a/src/mip/HighsMipSolver.cpp +++ b/src/mip/HighsMipSolver.cpp @@ -795,6 +795,11 @@ void HighsMipSolver::cleanupSolve() { " %.12g (row viol.)\n", solution_objective_, bound_violation_, integrality_violation_, row_violation_); + int ac_start = mipdata_->num_analytic_centre_start; + int ac_opt = mipdata_->num_analytic_centre_opt; + int ac_other = mipdata_->num_analytic_centre_other; + int ac_fail = mipdata_->num_analytic_centre_fail; + int ac_abort = ac_start - ac_opt - ac_other - ac_fail; highsLogUser(options_mip_->log_options, HighsLogType::kInfo, " Timing %.2f (total)\n" " %.2f (presolve)\n" @@ -806,8 +811,9 @@ void HighsMipSolver::cleanupSolve() { " %llu (strong br.)\n" " %llu (separation)\n" " %llu (heuristics)\n" - " AC failures %d\n" - " Max sub-MIP depth %d\n", + " Max sub-MIP depth %d\n" + " Analytic centre calculations (Optimal / other / abort / " + "fail) = (%d / %d / %d / %d) of %d\n", timer_.read(timer_.total_clock), analysis_.mipTimerRead(kMipClockPresolve), analysis_.mipTimerRead(kMipClockSolve), @@ -820,8 +826,12 @@ void HighsMipSolver::cleanupSolve() { (long long unsigned)mipdata_->sb_lp_iterations, (long long unsigned)mipdata_->sepa_lp_iterations, (long long unsigned)mipdata_->heuristic_lp_iterations, - int(mipdata_->num_analytic_centre_failures), - int(max_submip_level > 0 ? max_submip_level : 0)); + int(max_submip_level > 0 ? max_submip_level : 0), ac_opt, + ac_other, ac_abort, ac_fail, ac_start); + + highsLogUser(options_mip_->log_options, HighsLogType::kInfo, + "grepAcStats,%s,%d,%d,%d,%d,%d\n", model_->model_name_.c_str(), + ac_opt, ac_other, ac_abort, ac_fail, ac_start); analysis_.reportMipTimer(); diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index 5fe9bff4b9..f4af08b640 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -304,6 +304,7 @@ void HighsMipSolverData::startAnalyticCenterComputation( // due to early return in the root node evaluation assert(mipsolver.options_mip_->mip_compute_analytic_centre >= kMipAnalyticCentreCalulationOriginal); + const bool ac_logging = !mipsolver.submip; Highs ipm; if (mipsolver.options_mip_->mip_compute_analytic_centre == kMipAnalyticCentreCalulationOriginal) { @@ -317,8 +318,11 @@ void HighsMipSolverData::startAnalyticCenterComputation( } ipm.setOptionValue("presolve", "off"); ipm.setOptionValue("output_flag", false); - ipm.setOptionValue("output_flag", !mipsolver.submip); + // ipm.setOptionValue("output_flag", !mipsolver.submip); ipm.setOptionValue("ipm_iteration_limit", 200); + HighsInt kkt_iteration_limit = + std::max(100, mipsolver.model_->num_row_ / 1000); + ipm.setOptionValue("kkt_iteration_limit", kkt_iteration_limit); HighsLp lpmodel(*mipsolver.model_); lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); ipm.passModel(std::move(lpmodel)); @@ -326,14 +330,28 @@ void HighsMipSolverData::startAnalyticCenterComputation( // if (!mipsolver.submip) ipm.writeModel(mipsolver.model_->model_name_ + // "_ipm.mps"); + if (ac_logging) num_analytic_centre_start++; + double tt0 = mipsolver.timer_.read(mipsolver.timer_.total_clock); mipsolver.analysis_.mipTimerStart(kMipClockIpmSolveLp); ipm.run(); mipsolver.analysis_.mipTimerStop(kMipClockIpmSolveLp); - if (!mipsolver.submip) { - printf("grepAC: model; num_row; max CR counts are, %s, %d, %d, %d\n", - mipsolver.model_->model_name_.c_str(), int(ipm.getNumRow()), - int(ipm.getInfo().max_cr_iteration_count1), - int(ipm.getInfo().max_cr_iteration_count2)); + double tt1 = mipsolver.timer_.read(mipsolver.timer_.total_clock); + if (ac_logging) { + HighsModelStatus ac_status = ipm.getModelStatus(); + printf( + "grepAcLocal: model; num_row; CR limit; max CR counts; time and " + "status, %s, %d, %d, %d, %d, %g, %s\n", + mipsolver.model_->model_name_.c_str(), int(ipm.getNumRow()), + int(kkt_iteration_limit), int(ipm.getInfo().max_cr_iteration_count1), + int(ipm.getInfo().max_cr_iteration_count2), tt1 - tt0, + ipm.modelStatusToString(ac_status).c_str()); + if (ac_status == HighsModelStatus::kSolveError) { + num_analytic_centre_fail++; + } else if (ac_status == HighsModelStatus::kOptimal) { + num_analytic_centre_opt++; + } else { + num_analytic_centre_other++; + } } const std::vector& sol = ipm.getSolution().col_value; if (HighsInt(sol.size()) != mipsolver.numCol()) return; @@ -359,7 +377,6 @@ void HighsMipSolverData::finishAnalyticCenterComputation( } analyticCenterComputed = true; analyticCenterFailed = analyticCenterStatus == HighsModelStatus::kSolveError; - if (analyticCenterFailed) num_analytic_centre_failures++; if (analyticCenterStatus == HighsModelStatus::kOptimal) { HighsInt nfixed = 0; HighsInt nintfixed = 0; @@ -649,7 +666,10 @@ void HighsMipSolverData::init() { heuristic_lp_iterations_before_run = 0; sepa_lp_iterations_before_run = 0; sb_lp_iterations_before_run = 0; - num_analytic_centre_failures = 0; + num_analytic_centre_start = 0; + num_analytic_centre_opt = 0; + num_analytic_centre_other = 0; + num_analytic_centre_fail = 0; num_disp_lines = 0; numCliqueEntriesAfterPresolve = 0; numCliqueEntriesAfterFirstPresolve = 0; diff --git a/src/mip/HighsMipSolverData.h b/src/mip/HighsMipSolverData.h index 915d7c9761..ba8b8c3323 100644 --- a/src/mip/HighsMipSolverData.h +++ b/src/mip/HighsMipSolverData.h @@ -120,7 +120,10 @@ struct HighsMipSolverData { HighsInt numintegercols; HighsInt maxTreeSizeLog2; - HighsInt num_analytic_centre_failures; + HighsInt num_analytic_centre_start; + HighsInt num_analytic_centre_opt; + HighsInt num_analytic_centre_other; + HighsInt num_analytic_centre_fail; HighsCDouble pruned_treeweight; double avgrootlpiters; double last_disptime; @@ -187,7 +190,10 @@ struct HighsMipSolverData { rootlpsolobj(-kHighsInf), numintegercols(0), maxTreeSizeLog2(0), - num_analytic_centre_failures(0), + num_analytic_centre_start(0), + num_analytic_centre_opt(0), + num_analytic_centre_other(0), + num_analytic_centre_fail(0), pruned_treeweight(0), avgrootlpiters(0.0), last_disptime(0.0), diff --git a/src/mip/HighsPrimalHeuristics.cpp b/src/mip/HighsPrimalHeuristics.cpp index d3a90ef8b7..91f53a0ea2 100644 --- a/src/mip/HighsPrimalHeuristics.cpp +++ b/src/mip/HighsPrimalHeuristics.cpp @@ -137,6 +137,7 @@ bool HighsPrimalHeuristics::solveSubMip( // if (mipsolver.mipdata_->analyticCenterFailed) // submipoptions.mip_compute_analytic_centre = 0; + // setup solver and run it HighsSolution solution; solution.value_valid = false; From 150f4f31ab1397197c486ed771a6e1537bffaec5 Mon Sep 17 00:00:00 2001 From: jajhall Date: Tue, 26 Nov 2024 16:04:36 +0000 Subject: [PATCH 08/40] Fixed std::max error in HighsMipSolverDat.cpp:324 --- src/mip/HighsMipSolverData.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index f4af08b640..c1d6c8a45f 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -320,8 +320,8 @@ void HighsMipSolverData::startAnalyticCenterComputation( ipm.setOptionValue("output_flag", false); // ipm.setOptionValue("output_flag", !mipsolver.submip); ipm.setOptionValue("ipm_iteration_limit", 200); - HighsInt kkt_iteration_limit = - std::max(100, mipsolver.model_->num_row_ / 1000); + HighsInt kkt_iteration_limit = mipsolver.model_->num_row_ / 1000; + kkt_iteration_limit = std::max(100, kkt_iteration_limit); ipm.setOptionValue("kkt_iteration_limit", kkt_iteration_limit); HighsLp lpmodel(*mipsolver.model_); lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); From 7feb35417e1afe164485838bb30b76ba2ef2049e Mon Sep 17 00:00:00 2001 From: JAJHall Date: Tue, 26 Nov 2024 19:56:58 +0000 Subject: [PATCH 09/40] Cast 100 to HighsInt in HighsMipSolverData.cpp --- src/mip/HighsMipSolverData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index c1d6c8a45f..b0c0c4bf1c 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -321,7 +321,7 @@ void HighsMipSolverData::startAnalyticCenterComputation( // ipm.setOptionValue("output_flag", !mipsolver.submip); ipm.setOptionValue("ipm_iteration_limit", 200); HighsInt kkt_iteration_limit = mipsolver.model_->num_row_ / 1000; - kkt_iteration_limit = std::max(100, kkt_iteration_limit); + kkt_iteration_limit = std::max(HighsInt(100), kkt_iteration_limit); ipm.setOptionValue("kkt_iteration_limit", kkt_iteration_limit); HighsLp lpmodel(*mipsolver.model_); lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); From 57c5f4cc1512535399a879f87f0e61bd20dae1cf Mon Sep 17 00:00:00 2001 From: JAJHall Date: Wed, 27 Nov 2024 11:24:27 +0000 Subject: [PATCH 10/40] Now to introduce control of kkt iteration limit for phase 1 --- src/ipm/IpxWrapper.cpp | 1 + src/ipm/ipx/conjugate_residuals.cc | 7 +++-- src/ipm/ipx/control.h | 1 + src/ipm/ipx/ipx_internal.h | 1 + src/ipm/ipx/ipx_parameters.h | 1 + src/lp_data/HighsOptions.h | 7 +++++ src/mip/HighsMipSolverData.cpp | 43 +++++++++++++++++++++++------- src/mip/HighsPrimalHeuristics.cpp | 2 ++ 8 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index db48e9c7b7..4cd8f1e3ac 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -122,6 +122,7 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, parameters.ipm_maxiter = options.ipm_iteration_limit - highs_info.ipm_iteration_count; parameters.kkt_maxiter = options.kkt_iteration_limit; + parameters.kkt_logging = options.kkt_logging; // Determine if crossover is to be run or not // // When doing analytic centring calculations, crossover must not be diff --git a/src/ipm/ipx/conjugate_residuals.cc b/src/ipm/ipx/conjugate_residuals.cc index f7345def7c..26b89411b1 100644 --- a/src/ipm/ipx/conjugate_residuals.cc +++ b/src/ipm/ipx/conjugate_residuals.cc @@ -9,11 +9,12 @@ namespace ipx { ConjugateResiduals::ConjugateResiduals(const Control& control) : control_(control) {} -const bool cr_logging = false; - void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, double tol, const double* resscale, Int maxiter, Vector& lhs) { + + const bool cr_logging = control_.kkt_logging(); + const Int m = rhs.size(); Vector residual(m); // rhs - C*lhs Vector step(m); // update to lhs @@ -95,6 +96,8 @@ void ConjugateResiduals::Solve(LinearOperator& C, const Vector& rhs, void ConjugateResiduals::Solve(LinearOperator& C, LinearOperator& P, const Vector& rhs, double tol, const double* resscale, Int maxiter, Vector& lhs){ + const bool cr_logging = control_.kkt_logging(); + const Int m = rhs.size(); Vector residual(m); // rhs - C*lhs Vector sresidual(m); // preconditioned residual diff --git a/src/ipm/ipx/control.h b/src/ipm/ipx/control.h index 99e96307f0..0ea446b289 100644 --- a/src/ipm/ipx/control.h +++ b/src/ipm/ipx/control.h @@ -68,6 +68,7 @@ class Control { double ipm_optimality_tol() const { return parameters_.ipm_optimality_tol; } double ipm_drop_primal() const { return parameters_.ipm_drop_primal; } double ipm_drop_dual() const { return parameters_.ipm_drop_dual; } + bool kkt_logging() const { return parameters_.kkt_logging; } double kkt_tol() const { return parameters_.kkt_tol; } ipxint kkt_maxiter() const { return parameters_.kkt_maxiter; } ipxint crash_basis() const { return parameters_.crash_basis; } diff --git a/src/ipm/ipx/ipx_internal.h b/src/ipm/ipx/ipx_internal.h index d9a2133099..4b5a152016 100644 --- a/src/ipm/ipx/ipx_internal.h +++ b/src/ipm/ipx/ipx_internal.h @@ -31,6 +31,7 @@ struct Parameters : public ipx_parameters { ipm_optimality_tol = 1e-8; ipm_drop_primal = 1e-9; ipm_drop_dual = 1e-9; + kkt_logging = false; kkt_tol = 0.3; kkt_maxiter = -1; crash_basis = 1; diff --git a/src/ipm/ipx/ipx_parameters.h b/src/ipm/ipx/ipx_parameters.h index 590f0b90f0..388f35e7c8 100644 --- a/src/ipm/ipx/ipx_parameters.h +++ b/src/ipm/ipx/ipx_parameters.h @@ -28,6 +28,7 @@ struct ipx_parameters { double ipm_drop_dual; /* Linear solver */ + bool kkt_logging; ipxint kkt_maxiter; double kkt_tol; diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index 73c261a889..c2ffd33e2e 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -334,6 +334,7 @@ struct HighsOptionsStruct { // Options for IPM solver HighsInt ipm_iteration_limit; HighsInt kkt_iteration_limit; + bool kkt_logging; // Options for PDLP solver bool pdlp_native_termination; @@ -484,6 +485,7 @@ struct HighsOptionsStruct { log_to_console(false), ipm_iteration_limit(0), kkt_iteration_limit(0), + kkt_logging(false), pdlp_native_termination(false), pdlp_scaling(false), pdlp_iteration_limit(0), @@ -1083,6 +1085,11 @@ class HighsOptions : public HighsOptionsStruct { &ipm_iteration_limit, 0, kHighsIInf, kHighsIInf); records.push_back(record_int); + record_bool = new OptionRecordBool( + "kkt_logging", "Perform logging in CR solver for KKT in IPX: Default = false", + advanced, &kkt_logging, false); + records.push_back(record_bool); + record_int = new OptionRecordInt("kkt_iteration_limit", "Iteration limit for PCG in IPX IPM solver: " diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index b0c0c4bf1c..aa10e4a2d1 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -318,11 +318,22 @@ void HighsMipSolverData::startAnalyticCenterComputation( } ipm.setOptionValue("presolve", "off"); ipm.setOptionValue("output_flag", false); - // ipm.setOptionValue("output_flag", !mipsolver.submip); + // ipm.setOptionValue("output_flag", !mipsolver.submip); // 2049 unset this ultimately ipm.setOptionValue("ipm_iteration_limit", 200); - HighsInt kkt_iteration_limit = mipsolver.model_->num_row_ / 1000; - kkt_iteration_limit = std::max(HighsInt(100), kkt_iteration_limit); - ipm.setOptionValue("kkt_iteration_limit", kkt_iteration_limit); + // ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this ultimately + // + // kkt_iteration_limit1 is what's set internal to IPX to limit the + // CR iterations before the initial basis is computed, and should + // not be changed + HighsInt kkt_iteration_limit1 = 10 + mipsolver.model_->num_row_ / 20; + kkt_iteration_limit1 = std::max(HighsInt(500), kkt_iteration_limit1); + // kkt_iteration_limit2 is not set internal to IPX, so the default + // value is m+100, which is OK if you're desperate to solve an LP, + // but can make the use of the AC in MIP prohibitively expensive + HighsInt kkt_iteration_limit2 = mipsolver.model_->num_row_ / 1000; + kkt_iteration_limit2 = std::max(HighsInt(100), kkt_iteration_limit2); + // 2049 Set this ultimately + // ipm.setOptionValue("kkt_iteration_limit", kkt_iteration_limit2); HighsLp lpmodel(*mipsolver.model_); lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); ipm.passModel(std::move(lpmodel)); @@ -339,13 +350,20 @@ void HighsMipSolverData::startAnalyticCenterComputation( if (ac_logging) { HighsModelStatus ac_status = ipm.getModelStatus(); printf( - "grepAcLocal: model; num_row; CR limit; max CR counts; time and " - "status, %s, %d, %d, %d, %d, %g, %s\n", + "grepAcLocal: model; num_row; CR limits+counts; time and status," + "%s,%d," + "%d,%d," + // "%d,%d," + "%g,%s\n", mipsolver.model_->model_name_.c_str(), int(ipm.getNumRow()), - int(kkt_iteration_limit), int(ipm.getInfo().max_cr_iteration_count1), - int(ipm.getInfo().max_cr_iteration_count2), tt1 - tt0, + // int(kkt_iteration_limit1), + int(ipm.getInfo().max_cr_iteration_count1), + // int(kkt_iteration_limit2), + int(ipm.getInfo().max_cr_iteration_count2), + tt1 - tt0, ipm.modelStatusToString(ac_status).c_str()); - if (ac_status == HighsModelStatus::kSolveError) { + if (ac_status == HighsModelStatus::kSolveError || + ac_status == HighsModelStatus::kUnknown) { num_analytic_centre_fail++; } else if (ac_status == HighsModelStatus::kOptimal) { num_analytic_centre_opt++; @@ -376,7 +394,9 @@ void HighsMipSolverData::finishAnalyticCenterComputation( fflush(stdout); } analyticCenterComputed = true; - analyticCenterFailed = analyticCenterStatus == HighsModelStatus::kSolveError; + analyticCenterFailed = + analyticCenterStatus == HighsModelStatus::kSolveError || + analyticCenterStatus == HighsModelStatus::kUnknown; if (analyticCenterStatus == HighsModelStatus::kOptimal) { HighsInt nfixed = 0; HighsInt nintfixed = 0; @@ -413,6 +433,9 @@ void HighsMipSolverData::finishAnalyticCenterComputation( nfixed, nintfixed); mipsolver.mipdata_->domain.propagate(); if (mipsolver.mipdata_->domain.infeasible()) return; + } else if (!analyticCenterFailed) { + printf("HighsMipSolverData::finishAnalyticCenterComputation: analyticCenterStatus = %s\n", + lp.getLpSolver().modelStatusToString().c_str()); } } diff --git a/src/mip/HighsPrimalHeuristics.cpp b/src/mip/HighsPrimalHeuristics.cpp index 91f53a0ea2..50d6e066f7 100644 --- a/src/mip/HighsPrimalHeuristics.cpp +++ b/src/mip/HighsPrimalHeuristics.cpp @@ -135,6 +135,8 @@ bool HighsPrimalHeuristics::solveSubMip( // If the analytic centre calculation has failed for the parent MIP, // then don't perform it for the sub-MIP + // 2049 Set this ultimately + // // if (mipsolver.mipdata_->analyticCenterFailed) // submipoptions.mip_compute_analytic_centre = 0; From 39f712d532079a9a11da33bcefcd3d11653d3c43 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Wed, 27 Nov 2024 13:34:07 +0000 Subject: [PATCH 11/40] Test 100 MIP with proposed cr1 and cr2 limits --- src/ipm/IpxWrapper.cpp | 3 ++- src/ipm/ipx/control.h | 3 ++- src/ipm/ipx/ipx_internal.h | 3 ++- src/ipm/ipx/ipx_parameters.h | 3 ++- src/ipm/ipx/lp_solver.cc | 18 +++++++++++++---- src/lp_data/HighsOptions.h | 19 +++++++++++++----- src/mip/HighsMipSolverData.cpp | 33 +++++++++++++++++-------------- src/mip/HighsPrimalHeuristics.cpp | 3 +-- 8 files changed, 55 insertions(+), 30 deletions(-) diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 4cd8f1e3ac..4a4e3d4c76 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -121,7 +121,8 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, parameters.time_limit = options.time_limit - timer.readRunHighsClock(); parameters.ipm_maxiter = options.ipm_iteration_limit - highs_info.ipm_iteration_count; - parameters.kkt_maxiter = options.kkt_iteration_limit; + parameters.cr1_maxiter = options.cr1_iteration_limit; + parameters.cr2_maxiter = options.cr2_iteration_limit; parameters.kkt_logging = options.kkt_logging; // Determine if crossover is to be run or not // diff --git a/src/ipm/ipx/control.h b/src/ipm/ipx/control.h index 0ea446b289..c5b19969e0 100644 --- a/src/ipm/ipx/control.h +++ b/src/ipm/ipx/control.h @@ -70,7 +70,8 @@ class Control { double ipm_drop_dual() const { return parameters_.ipm_drop_dual; } bool kkt_logging() const { return parameters_.kkt_logging; } double kkt_tol() const { return parameters_.kkt_tol; } - ipxint kkt_maxiter() const { return parameters_.kkt_maxiter; } + ipxint cr1_maxiter() const { return parameters_.cr1_maxiter; } + ipxint cr2_maxiter() const { return parameters_.cr2_maxiter; } ipxint crash_basis() const { return parameters_.crash_basis; } double dependency_tol() const { return parameters_.dependency_tol; } double volume_tol() const { return parameters_.volume_tol; } diff --git a/src/ipm/ipx/ipx_internal.h b/src/ipm/ipx/ipx_internal.h index 4b5a152016..4fe05ea86f 100644 --- a/src/ipm/ipx/ipx_internal.h +++ b/src/ipm/ipx/ipx_internal.h @@ -33,7 +33,8 @@ struct Parameters : public ipx_parameters { ipm_drop_dual = 1e-9; kkt_logging = false; kkt_tol = 0.3; - kkt_maxiter = -1; + cr1_maxiter = -1; + cr2_maxiter = -1; crash_basis = 1; dependency_tol = 1e-6; volume_tol = 2.0; diff --git a/src/ipm/ipx/ipx_parameters.h b/src/ipm/ipx/ipx_parameters.h index 388f35e7c8..029b89f65c 100644 --- a/src/ipm/ipx/ipx_parameters.h +++ b/src/ipm/ipx/ipx_parameters.h @@ -29,7 +29,8 @@ struct ipx_parameters { /* Linear solver */ bool kkt_logging; - ipxint kkt_maxiter; + ipxint cr1_maxiter; + ipxint cr2_maxiter; double kkt_tol; /* Basis construction in IPM */ diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index 4ae972c92d..0d422e8c65 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -484,10 +484,20 @@ void LpSolver::RunInitialIPM(IPM& ipm) { Int switchiter = control_.switchiter(); if (switchiter < 0) { - // Switch iteration not specified by user. Run as long as KKT solver - // converges within min(500,10+m/20) iterations. + // Switch iteration not specified by user. Run as long as KKT + // solver converges within + // + // min(control_.cr1_maxiter(), 500,10+m/20) + // + // iterations, ignoring control_.cr1_maxiter() if negative Int m = model_.rows(); - kkt.maxiter(std::min(500l, (long) (10+m/20) )); + ipxint df_cr1_maxiter = std::min(500l, (long) (10+m/20)); + ipxint cr1_maxiter = df_cr1_maxiter; + ipxint control_cr1_maxiter = control_.cr1_maxiter(); + if (control_cr1_maxiter > 0) cr1_maxiter = std::min(control_cr1_maxiter, cr1_maxiter); + printf("LpSolver::RunInitialIPM Using kkt.maxiter = %d, from df_cr1_maxiter = %d and control_cr1_maxiter = %d\n", + int(cr1_maxiter), int(df_cr1_maxiter), int(control_cr1_maxiter)); + kkt.maxiter(cr1_maxiter); ipm.maxiter(control_.ipm_maxiter()); } else { ipm.maxiter(std::min(switchiter, control_.ipm_maxiter())); @@ -558,7 +568,7 @@ void LpSolver::RunMainIPM(IPM& ipm) { KKTSolverBasis kkt(control_, *basis_); Timer timer; ipm.maxiter(control_.ipm_maxiter()); - kkt.maxiter(control_.kkt_maxiter()); + kkt.maxiter(control_.cr2_maxiter()); ipm.Driver(&kkt, iterate_.get(), &info_); info_.time_ipm2 = timer.Elapsed(); } diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index c2ffd33e2e..359f179da1 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -333,7 +333,8 @@ struct HighsOptionsStruct { // Options for IPM solver HighsInt ipm_iteration_limit; - HighsInt kkt_iteration_limit; + HighsInt cr1_iteration_limit; + HighsInt cr2_iteration_limit; bool kkt_logging; // Options for PDLP solver @@ -484,7 +485,8 @@ struct HighsOptionsStruct { output_flag(false), log_to_console(false), ipm_iteration_limit(0), - kkt_iteration_limit(0), + cr1_iteration_limit(0), + cr2_iteration_limit(0), kkt_logging(false), pdlp_native_termination(false), pdlp_scaling(false), @@ -1091,10 +1093,17 @@ class HighsOptions : public HighsOptionsStruct { records.push_back(record_bool); record_int = - new OptionRecordInt("kkt_iteration_limit", - "Iteration limit for PCG in IPX IPM solver: " + new OptionRecordInt("cr1_iteration_limit", + "Iteration limit for initial CR in IPX IPM solver: " "default is -1, so set internally", - advanced, &kkt_iteration_limit, -1, -1, kHighsIInf); + advanced, &cr1_iteration_limit, -1, -1, kHighsIInf); + records.push_back(record_int); + + record_int = + new OptionRecordInt("cr2_iteration_limit", + "Iteration limit for main CR in IPX IPM solver: " + "default is -1, so set internally", + advanced, &cr2_iteration_limit, -1, -1, kHighsIInf); records.push_back(record_int); record_bool = new OptionRecordBool( diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index aa10e4a2d1..05f05cb46d 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -318,22 +318,25 @@ void HighsMipSolverData::startAnalyticCenterComputation( } ipm.setOptionValue("presolve", "off"); ipm.setOptionValue("output_flag", false); - // ipm.setOptionValue("output_flag", !mipsolver.submip); // 2049 unset this ultimately + ipm.setOptionValue("output_flag", !mipsolver.submip); // 2049 unset this ultimately ipm.setOptionValue("ipm_iteration_limit", 200); - // ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this ultimately + ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this ultimately // - // kkt_iteration_limit1 is what's set internal to IPX to limit the - // CR iterations before the initial basis is computed, and should - // not be changed - HighsInt kkt_iteration_limit1 = 10 + mipsolver.model_->num_row_ / 20; - kkt_iteration_limit1 = std::max(HighsInt(500), kkt_iteration_limit1); - // kkt_iteration_limit2 is not set internal to IPX, so the default + // cr1_iteration_limit is what's set internal to IPX to limit the + // CR iterations before the initial basis is computed, and perhaps + // should not be changed, but maybe 500 is too high + HighsInt cr1_iteration_limit = 10 + mipsolver.model_->num_row_ / 20; + cr1_iteration_limit = std::min(HighsInt(500), cr1_iteration_limit); // IPX internal default + cr1_iteration_limit = std::min(HighsInt(100), cr1_iteration_limit); //2049 set this ultimately + // 2049 set this ultimately + ipm.setOptionValue("cr1_iteration_limit", cr1_iteration_limit); + // cr2_iteration_limit is not set internal to IPX, so the default // value is m+100, which is OK if you're desperate to solve an LP, // but can make the use of the AC in MIP prohibitively expensive - HighsInt kkt_iteration_limit2 = mipsolver.model_->num_row_ / 1000; - kkt_iteration_limit2 = std::max(HighsInt(100), kkt_iteration_limit2); + HighsInt cr2_iteration_limit = 50 + mipsolver.model_->num_row_ / 1000; + cr2_iteration_limit = std::min(HighsInt(100), cr2_iteration_limit); // 2049 Set this ultimately - // ipm.setOptionValue("kkt_iteration_limit", kkt_iteration_limit2); + ipm.setOptionValue("cr2_iteration_limit", cr2_iteration_limit); HighsLp lpmodel(*mipsolver.model_); lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); ipm.passModel(std::move(lpmodel)); @@ -353,12 +356,12 @@ void HighsMipSolverData::startAnalyticCenterComputation( "grepAcLocal: model; num_row; CR limits+counts; time and status," "%s,%d," "%d,%d," - // "%d,%d," + "%d,%d," "%g,%s\n", mipsolver.model_->model_name_.c_str(), int(ipm.getNumRow()), - // int(kkt_iteration_limit1), + int(cr1_iteration_limit), int(ipm.getInfo().max_cr_iteration_count1), - // int(kkt_iteration_limit2), + int(cr2_iteration_limit), int(ipm.getInfo().max_cr_iteration_count2), tt1 - tt0, ipm.modelStatusToString(ac_status).c_str()); @@ -435,7 +438,7 @@ void HighsMipSolverData::finishAnalyticCenterComputation( if (mipsolver.mipdata_->domain.infeasible()) return; } else if (!analyticCenterFailed) { printf("HighsMipSolverData::finishAnalyticCenterComputation: analyticCenterStatus = %s\n", - lp.getLpSolver().modelStatusToString().c_str()); + lp.getLpSolver().modelStatusToString(analyticCenterStatus).c_str()); } } diff --git a/src/mip/HighsPrimalHeuristics.cpp b/src/mip/HighsPrimalHeuristics.cpp index 50d6e066f7..d05ebefe0b 100644 --- a/src/mip/HighsPrimalHeuristics.cpp +++ b/src/mip/HighsPrimalHeuristics.cpp @@ -137,8 +137,7 @@ bool HighsPrimalHeuristics::solveSubMip( // 2049 Set this ultimately // - // if (mipsolver.mipdata_->analyticCenterFailed) - // submipoptions.mip_compute_analytic_centre = 0; + if (mipsolver.mipdata_->analyticCenterFailed) submipoptions.mip_compute_analytic_centre = 0; // setup solver and run it HighsSolution solution; From b981b614dcf056810845e25b4af1620df742ab4e Mon Sep 17 00:00:00 2001 From: JAJHall Date: Wed, 27 Nov 2024 23:10:09 +0000 Subject: [PATCH 12/40] Extract current fill from IPX basis --- src/mip/HighsMipSolverData.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index 05f05cb46d..0e857d4315 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -333,8 +333,8 @@ void HighsMipSolverData::startAnalyticCenterComputation( // cr2_iteration_limit is not set internal to IPX, so the default // value is m+100, which is OK if you're desperate to solve an LP, // but can make the use of the AC in MIP prohibitively expensive - HighsInt cr2_iteration_limit = 50 + mipsolver.model_->num_row_ / 1000; - cr2_iteration_limit = std::min(HighsInt(100), cr2_iteration_limit); + HighsInt cr2_iteration_limit = 50 + mipsolver.model_->num_row_ / 50; + cr2_iteration_limit = std::min(HighsInt(500), cr2_iteration_limit); // 2049 Set this ultimately ipm.setOptionValue("cr2_iteration_limit", cr2_iteration_limit); HighsLp lpmodel(*mipsolver.model_); From 45545b755d7ecc5d62fb5bdd13a5b0c672450c1c Mon Sep 17 00:00:00 2001 From: JAJHall Date: Wed, 27 Nov 2024 23:43:27 +0000 Subject: [PATCH 13/40] Now extracting fill factors for IPX basis factorization --- src/ipm/ipx/basis.cc | 7 +++++++ src/ipm/ipx/basis.h | 1 + src/ipm/ipx/ipm.cc | 1 + src/ipm/ipx/kkt_solver.cc | 1 + src/ipm/ipx/kkt_solver.h | 4 ++++ src/ipm/ipx/kkt_solver_basis.h | 2 ++ src/ipm/ipx/kkt_solver_diag.h | 3 ++- 7 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ipm/ipx/basis.cc b/src/ipm/ipx/basis.cc index 607de407d1..994335163a 100644 --- a/src/ipm/ipx/basis.cc +++ b/src/ipm/ipx/basis.cc @@ -449,6 +449,13 @@ double Basis::time_update() const { return time_update_; } +double Basis::current_fill() const { + if (fill_factors_.empty()) + return 0.0; + Int num_factors = fill_factors_.size(); + return fill_factors_[num_factors-1]; +} + double Basis::mean_fill() const { if (fill_factors_.empty()) return 0.0; diff --git a/src/ipm/ipx/basis.h b/src/ipm/ipx/basis.h index 4ae358c171..19c8a6adca 100644 --- a/src/ipm/ipx/basis.h +++ b/src/ipm/ipx/basis.h @@ -219,6 +219,7 @@ class Basis { double time_ftran() const; // time FTRAN, including partial double time_btran() const; // time BTRAN, including partial double time_update() const; // time LU update + double current_fill() const; // Current LU fill factors double mean_fill() const; // geom. mean of LU fill factors double max_fill() const; // max LU fill factor diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index d8860f19fc..d417534e15 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -89,6 +89,7 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { kkt->Factorize(iterate, info); if (info->errflag) break; + printf("IPM::Driver fill factor = %g\n", kkt_->current_fill()); Predictor(step); if (info->errflag) break; diff --git a/src/ipm/ipx/kkt_solver.cc b/src/ipm/ipx/kkt_solver.cc index a828fcb0a3..c0d4da2397 100644 --- a/src/ipm/ipx/kkt_solver.cc +++ b/src/ipm/ipx/kkt_solver.cc @@ -19,6 +19,7 @@ void KKTSolver::Solve(const Vector& a, const Vector& b, double tol, Int KKTSolver::iterSum() const { return _iterSum(); } Int KKTSolver::iterMax() const { return _iterMax(); } Int KKTSolver::basis_changes() const { return _basis_changes(); } +double KKTSolver::current_fill() const {return _current_fill(); } const Basis* KKTSolver::basis() const { return _basis(); } } // namespace ipx diff --git a/src/ipm/ipx/kkt_solver.h b/src/ipm/ipx/kkt_solver.h index e5ab15bc22..7d3fe8f395 100644 --- a/src/ipm/ipx/kkt_solver.h +++ b/src/ipm/ipx/kkt_solver.h @@ -56,6 +56,9 @@ class KKTSolver { // call to Factorize(). Otherwise returns 0. Int basis_changes() const; + // + double current_fill() const; + // If a basis matrix is maintained, returns a pointer to it. // Otherwise returns NULL. const Basis* basis() const; @@ -67,6 +70,7 @@ class KKTSolver { virtual Int _iterSum() const = 0; virtual Int _iterMax() const = 0; virtual Int _basis_changes() const { return 0; } + virtual double _current_fill() const { return 0.0; } virtual const Basis* _basis() const { return nullptr; } }; diff --git a/src/ipm/ipx/kkt_solver_basis.h b/src/ipm/ipx/kkt_solver_basis.h index b46a89e5c2..b155211926 100644 --- a/src/ipm/ipx/kkt_solver_basis.h +++ b/src/ipm/ipx/kkt_solver_basis.h @@ -35,7 +35,9 @@ class KKTSolverBasis : public KKTSolver { Vector& x, Vector& y, Info* info) override; Int _iterSum() const override { return iter_sum_; } Int _iterMax() const override { return iter_max_; } + Int _basis_changes() const override { return basis_changes_; } + double _current_fill() const override { return _basis()->current_fill(); } const Basis* _basis() const override { return &basis_; } // Processes basic variables that are close to a bound by either pivoting diff --git a/src/ipm/ipx/kkt_solver_diag.h b/src/ipm/ipx/kkt_solver_diag.h index aaa18cfb85..6de0e94523 100644 --- a/src/ipm/ipx/kkt_solver_diag.h +++ b/src/ipm/ipx/kkt_solver_diag.h @@ -24,7 +24,8 @@ class KKTSolverDiag : public KKTSolver { Int maxiter() const { return maxiter_; } void maxiter(Int new_maxiter) { maxiter_ = new_maxiter; } - + // double current_fill() const { return 0; } + private: void _Factorize(Iterate* iterate, Info* info) override; void _Solve(const Vector& a, const Vector& b, double tol, From b6f29b84ff06049768f0c2c44c6640963e6d6278 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Thu, 28 Nov 2024 11:32:18 +0000 Subject: [PATCH 14/40] Reporting IPX fill factor, and added timeout to AC solve --- src/ipm/ipx/ipm.cc | 9 ++++++++- src/mip/HighsMipSolverData.cpp | 20 ++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index d417534e15..b67aa27193 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -89,7 +89,6 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { kkt->Factorize(iterate, info); if (info->errflag) break; - printf("IPM::Driver fill factor = %g\n", kkt_->current_fill()); Predictor(step); if (info->errflag) break; @@ -97,6 +96,14 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { if (info->errflag) break; MakeStep(step); + // + std::stringstream h_logging_stream; + h_logging_stream << "IPM::Driver fill factor = " << + kkt_->current_fill() << "; iter (sum = " << + kkt_->iterSum() << ", max = " << + kkt_->iterMax() << ")\n"; + control_.hLog(h_logging_stream); + // info->iter++; PrintOutput(); } diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index 0e857d4315..539495e8be 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -320,7 +320,12 @@ void HighsMipSolverData::startAnalyticCenterComputation( ipm.setOptionValue("output_flag", false); ipm.setOptionValue("output_flag", !mipsolver.submip); // 2049 unset this ultimately ipm.setOptionValue("ipm_iteration_limit", 200); - ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this ultimately + double time_available = + std::max(mipsolver.options_mip_->time_limit - + mipsolver.timer_.read(mipsolver.timer_.total_clock), + 0.1); + ipm.setOptionValue("time_limit", time_available); + // ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this ultimately // // cr1_iteration_limit is what's set internal to IPX to limit the // CR iterations before the initial basis is computed, and perhaps @@ -329,20 +334,19 @@ void HighsMipSolverData::startAnalyticCenterComputation( cr1_iteration_limit = std::min(HighsInt(500), cr1_iteration_limit); // IPX internal default cr1_iteration_limit = std::min(HighsInt(100), cr1_iteration_limit); //2049 set this ultimately // 2049 set this ultimately - ipm.setOptionValue("cr1_iteration_limit", cr1_iteration_limit); + // ipm.setOptionValue("cr1_iteration_limit", cr1_iteration_limit); // cr2_iteration_limit is not set internal to IPX, so the default // value is m+100, which is OK if you're desperate to solve an LP, // but can make the use of the AC in MIP prohibitively expensive HighsInt cr2_iteration_limit = 50 + mipsolver.model_->num_row_ / 50; cr2_iteration_limit = std::min(HighsInt(500), cr2_iteration_limit); // 2049 Set this ultimately - ipm.setOptionValue("cr2_iteration_limit", cr2_iteration_limit); + // ipm.setOptionValue("cr2_iteration_limit", cr2_iteration_limit); HighsLp lpmodel(*mipsolver.model_); lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); ipm.passModel(std::move(lpmodel)); - // if (!mipsolver.submip) ipm.writeModel(mipsolver.model_->model_name_ + - // "_ipm.mps"); + if (!mipsolver.submip) ipm.writeModel(mipsolver.model_->model_name_ + "_ipm.mps"); if (ac_logging) num_analytic_centre_start++; double tt0 = mipsolver.timer_.read(mipsolver.timer_.total_clock); @@ -1161,9 +1165,9 @@ double HighsMipSolverData::transformNewIntegerFeasibleSolution( } this->total_repair_lp++; double time_available = - std::max(mipsolver.options_mip_->time_limit - - mipsolver.timer_.read(mipsolver.timer_.total_clock), - 0.1); + std::max(mipsolver.options_mip_->time_limit - + mipsolver.timer_.read(mipsolver.timer_.total_clock), + 0.1); Highs tmpSolver; const bool debug_report = false; if (debug_report) { From d05a2d64f711b964634cb252eff7572335ab1964 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Thu, 28 Nov 2024 20:29:39 +0000 Subject: [PATCH 15/40] Added lp_data/HighsSolutionStats.h --- cmake/sources-python.cmake | 1 + cmake/sources.cmake | 1 + src/lp_data/HStruct.h | 14 -------------- src/simplex/HEkk.h | 1 + 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/cmake/sources-python.cmake b/cmake/sources-python.cmake index df2ff5a223..b6d3349a3e 100644 --- a/cmake/sources-python.cmake +++ b/cmake/sources-python.cmake @@ -311,6 +311,7 @@ set(highs_headers_python src/lp_data/HighsRanging.h src/lp_data/HighsSolution.h src/lp_data/HighsSolutionDebug.h + src/lp_data/HighsSolutionStats.h src/lp_data/HighsSolve.h src/lp_data/HighsStatus.h src/lp_data/HStruct.h diff --git a/cmake/sources.cmake b/cmake/sources.cmake index 96e84b7f5b..7335a2e83d 100644 --- a/cmake/sources.cmake +++ b/cmake/sources.cmake @@ -315,6 +315,7 @@ set(highs_headers lp_data/HighsRanging.h lp_data/HighsSolution.h lp_data/HighsSolutionDebug.h + lp_data/HighsSolutionStats.h lp_data/HighsSolve.h lp_data/HighsStatus.h lp_data/HStruct.h diff --git a/src/lp_data/HStruct.h b/src/lp_data/HStruct.h index 35a0b0ee42..3963d9df1c 100644 --- a/src/lp_data/HStruct.h +++ b/src/lp_data/HStruct.h @@ -154,18 +154,4 @@ 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 std::string message = "") const; - void initialise(const HighsInt iteration_count_ = 0); -}; - #endif /* LP_DATA_HSTRUCT_H_ */ diff --git a/src/simplex/HEkk.h b/src/simplex/HEkk.h index 0b253bebc0..b3b163a769 100644 --- a/src/simplex/HEkk.h +++ b/src/simplex/HEkk.h @@ -17,6 +17,7 @@ #include "lp_data/HighsCallback.h" #include "simplex/HSimplexNla.h" #include "simplex/HighsSimplexAnalysis.h" +#include "lp_data/HighsSolutionStats.h" #include "util/HSet.h" #include "util/HighsHash.h" #include "util/HighsRandom.h" From 4f393aaa8369a8e525156a8fcd7ad12c287d8c1f Mon Sep 17 00:00:00 2001 From: JAJHall Date: Fri, 29 Nov 2024 09:40:49 +0000 Subject: [PATCH 16/40] Now including simplex stats in IPX --- src/Highs.h | 3 +++ src/ipm/IpxWrapper.cpp | 16 ++++++++++----- src/ipm/IpxWrapper.h | 6 ++++-- src/ipm/ipx/ipx_parameters.h | 5 ++++- src/lp_data/HighsSolutionStats.h | 35 ++++++++++++++++++++++++++++++++ src/mip/HighsLpRelaxation.cpp | 8 ++++++-- src/mip/HighsMipSolverData.cpp | 4 ++++ src/simplex/HEkk.cpp | 4 ++++ src/simplex/HEkk.h | 1 + 9 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 src/lp_data/HighsSolutionStats.h diff --git a/src/Highs.h b/src/Highs.h index 70d800a5de..286eb002ed 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1219,6 +1219,9 @@ class Highs { const HighsSimplexStats& getSimplexStats() const { return ekk_instance_.getSimplexStats(); } + void passSimplexStats(const HighsSimplexStats simplex_stats) { + return ekk_instance_.passSimplexStats(simplex_stats); + } void reportSimplexStats(FILE* file) const { ekk_instance_.reportSimplexStats(file); } diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 4a4e3d4c76..0ab4e58ebc 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -23,15 +23,19 @@ using std::min; HighsStatus solveLpIpx(HighsLpSolverObject& solver_object) { return solveLpIpx(solver_object.options_, solver_object.timer_, - solver_object.lp_, solver_object.basis_, - solver_object.solution_, solver_object.model_status_, - solver_object.highs_info_, solver_object.callback_); + solver_object.lp_, solver_object.ekk_instance_, + solver_object.basis_, solver_object.solution_, + solver_object.model_status_, + solver_object.highs_info_, + solver_object.callback_); } HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, - const HighsLp& lp, HighsBasis& highs_basis, + const HighsLp& lp, const HEkk& ekk_instance, + HighsBasis& highs_basis, HighsSolution& highs_solution, - HighsModelStatus& model_status, HighsInfo& highs_info, + HighsModelStatus& model_status, + HighsInfo& highs_info, HighsCallback& callback) { // Use IPX to try to solve the LP // @@ -149,6 +153,8 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, parameters.max_centring_steps = options.max_centring_steps; parameters.centring_ratio_tolerance = options.centring_ratio_tolerance; + parameters.simplex_stats = ekk_instance.getSimplexStats(); + // Set the internal IPX parameters lps.SetParameters(parameters); diff --git a/src/ipm/IpxWrapper.h b/src/ipm/IpxWrapper.h index 660165d82d..6f8a75d8b3 100644 --- a/src/ipm/IpxWrapper.h +++ b/src/ipm/IpxWrapper.h @@ -25,9 +25,11 @@ HighsStatus solveLpIpx(HighsLpSolverObject& solver_object); HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, - const HighsLp& lp, HighsBasis& highs_basis, + const HighsLp& lp, const HEkk& ekk_instance, + HighsBasis& highs_basis, HighsSolution& highs_solution, - HighsModelStatus& model_status, HighsInfo& highs_info, + HighsModelStatus& model_status, + HighsInfo& highs_info, HighsCallback& callback); void fillInIpxData(const HighsLp& lp, ipx::Int& num_col, ipx::Int& num_row, diff --git a/src/ipm/ipx/ipx_parameters.h b/src/ipm/ipx/ipx_parameters.h index 029b89f65c..1fc4af238a 100644 --- a/src/ipm/ipx/ipx_parameters.h +++ b/src/ipm/ipx/ipx_parameters.h @@ -2,6 +2,7 @@ #define IPX_PARAMETERS_H_ #include "io/HighsIO.h" +#include "lp_data/HighsSolutionStats.h" #include "ipm/ipx/ipx_config.h" #include @@ -68,7 +69,9 @@ struct ipx_parameters { /* HiGHS logging parameters */ bool highs_logging; const HighsLogOptions* log_options; - + + /* Simplex solution stats */ + HighsSimplexStats simplex_stats; }; #ifdef __cplusplus diff --git a/src/lp_data/HighsSolutionStats.h b/src/lp_data/HighsSolutionStats.h new file mode 100644 index 0000000000..ea43a9522a --- /dev/null +++ b/src/lp_data/HighsSolutionStats.h @@ -0,0 +1,35 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* This file is part of the HiGHS linear optimization suite */ +/* */ +/* Written and engineered 2008-2024 by Julian Hall, Ivet Galabova, */ +/* Leona Gottwald and Michael Feldmeier */ +/* */ +/* Available as open-source under the MIT License */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/**@file lp_data/HighsSolutionStats.h + * @brief Structs for HiGHS + */ +#ifndef LP_DATA_HIGHSSOLUTIONSTATS_H_ +#define LP_DATA_HIGHSSOLUTIONSTATS_H_ + +#include + +#include "lp_data/HConst.h" + +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 std::string message = "") const; + void initialise(const HighsInt iteration_count_ = 0); +}; + +#endif /* LP_DATA_HIGHSSOLUTIONSTATS_H_ */ diff --git a/src/mip/HighsLpRelaxation.cpp b/src/mip/HighsLpRelaxation.cpp index 4288f99437..ff2fc7d3b1 100644 --- a/src/mip/HighsLpRelaxation.cpp +++ b/src/mip/HighsLpRelaxation.cpp @@ -1064,9 +1064,11 @@ HighsLpRelaxation::Status HighsLpRelaxation::run(bool resolve_on_error) { int(lpsolver.getNumCol()), int(lpsolver.getNumRow())); } } - const bool solver_logging = false; + const bool solver_logging = false;//!mipsolver.submip && !valid_basis; const bool detailed_simplex_logging = false; - if (solver_logging) lpsolver.setOptionValue("output_flag", true); + if (solver_logging) { + lpsolver.setOptionValue("output_flag", true); + } if (detailed_simplex_logging) { lpsolver.setOptionValue("output_flag", true); lpsolver.setOptionValue("log_dev_level", kHighsLogDevLevelVerbose); @@ -1076,6 +1078,8 @@ HighsLpRelaxation::Status HighsLpRelaxation::run(bool resolve_on_error) { mipsolver.analysis_.mipTimerStart(simplex_solve_clock); HighsStatus callstatus = lpsolver.run(); + lpsolver.setOptionValue("output_flag", false); // !fix-2049 + // if (solver_logging) lpsolver.reportSimplexStats(stdout); // !fix-2049 mipsolver.analysis_.mipTimerStop(simplex_solve_clock); const HighsInfo& info = lpsolver.getInfo(); diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index 1f1b2830cf..8a07cfb5c4 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -342,6 +342,10 @@ void HighsMipSolverData::startAnalyticCenterComputation( cr2_iteration_limit = std::min(HighsInt(500), cr2_iteration_limit); // 2049 Set this ultimately // ipm.setOptionValue("cr2_iteration_limit", cr2_iteration_limit); + + HighsSimplexStats simplex_stats = lp.getLpSolver().getSimplexStats(); + if (!mipsolver.submip) simplex_stats.report(stdout); + ipm.passSimplexStats(simplex_stats); HighsLp lpmodel(*mipsolver.model_); lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); ipm.passModel(std::move(lpmodel)); diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index caa4e641e0..ca743c1f98 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -4421,6 +4421,10 @@ void HEkk::unitBtranResidual(const HighsInt row_out, const HVector& row_ep, } } +void HEkk::passSimplexStats(const HighsSimplexStats simplex_stats) { + this->simplex_stats_ = simplex_stats; +} + 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); diff --git a/src/simplex/HEkk.h b/src/simplex/HEkk.h index b3b163a769..d62a4e9fe9 100644 --- a/src/simplex/HEkk.h +++ b/src/simplex/HEkk.h @@ -158,6 +158,7 @@ class HEkk { const vector& rowUpper); const HighsSimplexStats& getSimplexStats() const { return simplex_stats_; } + void passSimplexStats(const HighsSimplexStats simplex_stats); void initialiseSimplexStats() { simplex_stats_.initialise(iteration_count_); } void reportSimplexStats(FILE* file, const std::string message = "") const { simplex_stats_.report(file, message); From 54ce3ae06ca8e0f7cc036eaa92a20154e4c4c3be Mon Sep 17 00:00:00 2001 From: JAJHall Date: Fri, 29 Nov 2024 09:41:11 +0000 Subject: [PATCH 17/40] Formatted --- src/ipm/IpxWrapper.cpp | 13 +++----- src/ipm/IpxWrapper.h | 6 ++-- src/lp_data/HighsOptions.h | 3 +- src/mip/HighsLpRelaxation.cpp | 4 +-- src/mip/HighsMipSolverData.cpp | 52 +++++++++++++++++-------------- src/mip/HighsPrimalHeuristics.cpp | 3 +- src/simplex/HEkk.h | 2 +- 7 files changed, 42 insertions(+), 41 deletions(-) diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 0ab4e58ebc..7a073eeba5 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -24,18 +24,15 @@ using std::min; HighsStatus solveLpIpx(HighsLpSolverObject& solver_object) { return solveLpIpx(solver_object.options_, solver_object.timer_, solver_object.lp_, solver_object.ekk_instance_, - solver_object.basis_, solver_object.solution_, - solver_object.model_status_, - solver_object.highs_info_, - solver_object.callback_); + solver_object.basis_, solver_object.solution_, + solver_object.model_status_, solver_object.highs_info_, + solver_object.callback_); } HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, const HighsLp& lp, const HEkk& ekk_instance, - HighsBasis& highs_basis, - HighsSolution& highs_solution, - HighsModelStatus& model_status, - HighsInfo& highs_info, + HighsBasis& highs_basis, HighsSolution& highs_solution, + HighsModelStatus& model_status, HighsInfo& highs_info, HighsCallback& callback) { // Use IPX to try to solve the LP // diff --git a/src/ipm/IpxWrapper.h b/src/ipm/IpxWrapper.h index 6f8a75d8b3..792cf05346 100644 --- a/src/ipm/IpxWrapper.h +++ b/src/ipm/IpxWrapper.h @@ -26,10 +26,8 @@ HighsStatus solveLpIpx(HighsLpSolverObject& solver_object); HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, const HighsLp& lp, const HEkk& ekk_instance, - HighsBasis& highs_basis, - HighsSolution& highs_solution, - HighsModelStatus& model_status, - HighsInfo& highs_info, + HighsBasis& highs_basis, HighsSolution& highs_solution, + HighsModelStatus& model_status, HighsInfo& highs_info, HighsCallback& callback); void fillInIpxData(const HighsLp& lp, ipx::Int& num_col, ipx::Int& num_row, diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index 359f179da1..6ae321368d 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -1088,7 +1088,8 @@ class HighsOptions : public HighsOptionsStruct { records.push_back(record_int); record_bool = new OptionRecordBool( - "kkt_logging", "Perform logging in CR solver for KKT in IPX: Default = false", + "kkt_logging", + "Perform logging in CR solver for KKT in IPX: Default = false", advanced, &kkt_logging, false); records.push_back(record_bool); diff --git a/src/mip/HighsLpRelaxation.cpp b/src/mip/HighsLpRelaxation.cpp index ff2fc7d3b1..73b675917c 100644 --- a/src/mip/HighsLpRelaxation.cpp +++ b/src/mip/HighsLpRelaxation.cpp @@ -1064,7 +1064,7 @@ HighsLpRelaxation::Status HighsLpRelaxation::run(bool resolve_on_error) { int(lpsolver.getNumCol()), int(lpsolver.getNumRow())); } } - const bool solver_logging = false;//!mipsolver.submip && !valid_basis; + const bool solver_logging = false; //! mipsolver.submip && !valid_basis; const bool detailed_simplex_logging = false; if (solver_logging) { lpsolver.setOptionValue("output_flag", true); @@ -1078,7 +1078,7 @@ HighsLpRelaxation::Status HighsLpRelaxation::run(bool resolve_on_error) { mipsolver.analysis_.mipTimerStart(simplex_solve_clock); HighsStatus callstatus = lpsolver.run(); - lpsolver.setOptionValue("output_flag", false); // !fix-2049 + lpsolver.setOptionValue("output_flag", false); // !fix-2049 // if (solver_logging) lpsolver.reportSimplexStats(stdout); // !fix-2049 mipsolver.analysis_.mipTimerStop(simplex_solve_clock); diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index 8a07cfb5c4..f03114f2ab 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -318,22 +318,26 @@ void HighsMipSolverData::startAnalyticCenterComputation( } ipm.setOptionValue("presolve", "off"); ipm.setOptionValue("output_flag", false); - ipm.setOptionValue("output_flag", !mipsolver.submip); // 2049 unset this ultimately + ipm.setOptionValue("output_flag", + !mipsolver.submip); // 2049 unset this ultimately ipm.setOptionValue("ipm_iteration_limit", 200); double time_available = - std::max(mipsolver.options_mip_->time_limit - - mipsolver.timer_.read(mipsolver.timer_.total_clock), - 0.1); + std::max(mipsolver.options_mip_->time_limit - + mipsolver.timer_.read(mipsolver.timer_.total_clock), + 0.1); ipm.setOptionValue("time_limit", time_available); - // ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this ultimately + // ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this + // ultimately // // cr1_iteration_limit is what's set internal to IPX to limit the // CR iterations before the initial basis is computed, and perhaps // should not be changed, but maybe 500 is too high HighsInt cr1_iteration_limit = 10 + mipsolver.model_->num_row_ / 20; - cr1_iteration_limit = std::min(HighsInt(500), cr1_iteration_limit); // IPX internal default - cr1_iteration_limit = std::min(HighsInt(100), cr1_iteration_limit); //2049 set this ultimately - // 2049 set this ultimately + cr1_iteration_limit = + std::min(HighsInt(500), cr1_iteration_limit); // IPX internal default + cr1_iteration_limit = std::min( + HighsInt(100), cr1_iteration_limit); // 2049 set this ultimately + // 2049 set this ultimately // ipm.setOptionValue("cr1_iteration_limit", cr1_iteration_limit); // cr2_iteration_limit is not set internal to IPX, so the default // value is m+100, which is OK if you're desperate to solve an LP, @@ -350,7 +354,8 @@ void HighsMipSolverData::startAnalyticCenterComputation( lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); ipm.passModel(std::move(lpmodel)); - if (!mipsolver.submip) ipm.writeModel(mipsolver.model_->model_name_ + "_ipm.mps"); + if (!mipsolver.submip) + ipm.writeModel(mipsolver.model_->model_name_ + "_ipm.mps"); if (ac_logging) num_analytic_centre_start++; double tt0 = mipsolver.timer_.read(mipsolver.timer_.total_clock); @@ -364,17 +369,14 @@ void HighsMipSolverData::startAnalyticCenterComputation( "grepAcLocal: model; num_row; CR limits+counts; time and status," "%s,%d," "%d,%d," - "%d,%d," + "%d,%d," "%g,%s\n", mipsolver.model_->model_name_.c_str(), int(ipm.getNumRow()), - int(cr1_iteration_limit), - int(ipm.getInfo().max_cr_iteration_count1), - int(cr2_iteration_limit), - int(ipm.getInfo().max_cr_iteration_count2), - tt1 - tt0, - ipm.modelStatusToString(ac_status).c_str()); + int(cr1_iteration_limit), int(ipm.getInfo().max_cr_iteration_count1), + int(cr2_iteration_limit), int(ipm.getInfo().max_cr_iteration_count2), + tt1 - tt0, ipm.modelStatusToString(ac_status).c_str()); if (ac_status == HighsModelStatus::kSolveError || - ac_status == HighsModelStatus::kUnknown) { + ac_status == HighsModelStatus::kUnknown) { num_analytic_centre_fail++; } else if (ac_status == HighsModelStatus::kOptimal) { num_analytic_centre_opt++; @@ -406,8 +408,8 @@ void HighsMipSolverData::finishAnalyticCenterComputation( } analyticCenterComputed = true; analyticCenterFailed = - analyticCenterStatus == HighsModelStatus::kSolveError || - analyticCenterStatus == HighsModelStatus::kUnknown; + analyticCenterStatus == HighsModelStatus::kSolveError || + analyticCenterStatus == HighsModelStatus::kUnknown; if (analyticCenterStatus == HighsModelStatus::kOptimal) { HighsInt nfixed = 0; HighsInt nintfixed = 0; @@ -445,8 +447,10 @@ void HighsMipSolverData::finishAnalyticCenterComputation( mipsolver.mipdata_->domain.propagate(); if (mipsolver.mipdata_->domain.infeasible()) return; } else if (!analyticCenterFailed) { - printf("HighsMipSolverData::finishAnalyticCenterComputation: analyticCenterStatus = %s\n", - lp.getLpSolver().modelStatusToString(analyticCenterStatus).c_str()); + printf( + "HighsMipSolverData::finishAnalyticCenterComputation: " + "analyticCenterStatus = %s\n", + lp.getLpSolver().modelStatusToString(analyticCenterStatus).c_str()); } } @@ -1169,9 +1173,9 @@ double HighsMipSolverData::transformNewIntegerFeasibleSolution( } this->total_repair_lp++; double time_available = - std::max(mipsolver.options_mip_->time_limit - - mipsolver.timer_.read(mipsolver.timer_.total_clock), - 0.1); + std::max(mipsolver.options_mip_->time_limit - + mipsolver.timer_.read(mipsolver.timer_.total_clock), + 0.1); Highs tmpSolver; const bool debug_report = false; if (debug_report) { diff --git a/src/mip/HighsPrimalHeuristics.cpp b/src/mip/HighsPrimalHeuristics.cpp index 1812803af0..8b147fd49d 100644 --- a/src/mip/HighsPrimalHeuristics.cpp +++ b/src/mip/HighsPrimalHeuristics.cpp @@ -137,7 +137,8 @@ bool HighsPrimalHeuristics::solveSubMip( // 2049 Set this ultimately // - if (mipsolver.mipdata_->analyticCenterFailed) submipoptions.mip_compute_analytic_centre = 0; + if (mipsolver.mipdata_->analyticCenterFailed) + submipoptions.mip_compute_analytic_centre = 0; // setup solver and run it HighsSolution solution; diff --git a/src/simplex/HEkk.h b/src/simplex/HEkk.h index d62a4e9fe9..62594dd822 100644 --- a/src/simplex/HEkk.h +++ b/src/simplex/HEkk.h @@ -15,9 +15,9 @@ #define SIMPLEX_HEKK_H_ #include "lp_data/HighsCallback.h" +#include "lp_data/HighsSolutionStats.h" #include "simplex/HSimplexNla.h" #include "simplex/HighsSimplexAnalysis.h" -#include "lp_data/HighsSolutionStats.h" #include "util/HSet.h" #include "util/HighsHash.h" #include "util/HighsRandom.h" From 17c4c8ac05e41b2ef6436f7d33f5f65ca00ad93c Mon Sep 17 00:00:00 2001 From: Julian Hall Date: Fri, 29 Nov 2024 13:03:40 +0000 Subject: [PATCH 18/40] Introduced simplex_stats_ and presolved_lp_simplex_stats_ into Highs.h --- check/TestLpSolvers.cpp | 36 +++++++++++++++++++++++++++++------- src/Highs.h | 13 ++++++++++--- src/lp_data/Highs.cpp | 7 +++++++ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/check/TestLpSolvers.cpp b/check/TestLpSolvers.cpp index 49da8322b0..6b7d5d1c8c 100644 --- a/check/TestLpSolvers.cpp +++ b/check/TestLpSolvers.cpp @@ -648,23 +648,45 @@ TEST_CASE("simplex-stats", "[highs_lp_solver]") { Highs h; const HighsSimplexStats& simplex_stats = h.getSimplexStats(); + const HighsSimplexStats& presolved_lp_simplex_stats = + h.getPresolvedLpSimplexStats(); h.setOptionValue("output_flag", dev_run); + // std::string model = "dcp2"; + std::string model = "adlittle"; std::string model_file = - std::string(HIGHS_DIR) + "/check/instances/adlittle.mps"; + std::string(HIGHS_DIR) + "/check/instances/" + model + ".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.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 (simplex_stats.iteration_count > 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); + } else { + 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); + REQUIRE(presolved_lp_simplex_stats.valid); + REQUIRE(presolved_lp_simplex_stats.iteration_count > 0); + REQUIRE(presolved_lp_simplex_stats.num_invert > 0); + REQUIRE(presolved_lp_simplex_stats.last_invert_num_el > 0); + REQUIRE(presolved_lp_simplex_stats.last_factored_basis_num_el > 0); + REQUIRE(presolved_lp_simplex_stats.col_aq_density > 0); + REQUIRE(presolved_lp_simplex_stats.row_ep_density > 0); + REQUIRE(presolved_lp_simplex_stats.row_ap_density > 0); + REQUIRE(presolved_lp_simplex_stats.row_DSE_density > 0); + if (dev_run) h.reportPresolvedLpSimplexStats(stdout); + h.clearSolver(); h.setOptionValue("presolve", kHighsOffString); REQUIRE(h.run() == HighsStatus::kOk); diff --git a/src/Highs.h b/src/Highs.h index 70d800a5de..b2553f21c1 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1216,11 +1216,16 @@ class Highs { // Start of advanced methods for HiGHS MIP solver - const HighsSimplexStats& getSimplexStats() const { - return ekk_instance_.getSimplexStats(); + const HighsSimplexStats& getPresolvedLpSimplexStats() const { + return presolved_lp_simplex_stats_; } + void reportPresolvedLpSimplexStats(FILE* file) const { + presolved_lp_simplex_stats_.report(file, "Presolved LP"); + } + + const HighsSimplexStats& getSimplexStats() const { return simplex_stats_; } void reportSimplexStats(FILE* file) const { - ekk_instance_.reportSimplexStats(file); + simplex_stats_.report(file, "Original LP"); } /** @@ -1438,6 +1443,8 @@ class Highs { HighsSparseMatrix standard_form_matrix_; HEkk ekk_instance_; + HighsSimplexStats simplex_stats_; + HighsSimplexStats presolved_lp_simplex_stats_; HighsPresolveLog presolve_log_; diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index 6352a68a09..f9119a9ba7 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -1275,6 +1275,7 @@ HighsStatus Highs::solve() { solveLp(incumbent_lp, "Solving LP without presolve, or with basis, or unconstrained", this_solve_original_lp_time); + simplex_stats_ = this->ekk_instance_.getSimplexStats(); return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); if (return_status == HighsStatus::kError) @@ -1318,6 +1319,7 @@ HighsStatus Highs::solve() { ekk_instance_.lp_name_ = "Original LP"; solveLp(incumbent_lp, "Not presolved: solving the LP", this_solve_original_lp_time); + simplex_stats_ = this->ekk_instance_.getSimplexStats(); return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); if (return_status == HighsStatus::kError) @@ -1330,6 +1332,7 @@ HighsStatus Highs::solve() { reportPresolveReductions(log_options, incumbent_lp, false); solveLp(incumbent_lp, "Problem not reduced by presolve: solving the LP", this_solve_original_lp_time); + simplex_stats_ = this->ekk_instance_.getSimplexStats(); return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); if (return_status == HighsStatus::kError) @@ -1378,6 +1381,7 @@ HighsStatus Highs::solve() { options_.objective_bound = kHighsInf; solveLp(reduced_lp, "Solving the presolved LP", this_solve_presolved_lp_time); + presolved_lp_simplex_stats_ = this->ekk_instance_.getSimplexStats(); if (ekk_instance_.status_.initialised_for_solve) { // Record the pivot threshold resulting from solving the presolved LP // with simplex @@ -1442,6 +1446,7 @@ HighsStatus Highs::solve() { "Solving the original LP with primal simplex " "to determine infeasible or unbounded", this_solve_original_lp_time); + simplex_stats_ = this->ekk_instance_.getSimplexStats(); // Recover the options options_ = save_options; if (return_status == HighsStatus::kError) @@ -1579,6 +1584,7 @@ HighsStatus Highs::solve() { solveLp(incumbent_lp, "Solving the original LP from the solution after postsolve", this_solve_original_lp_time); + simplex_stats_ = this->ekk_instance_.getSimplexStats(); // Determine the iteration count postsolve_iteration_count += info_.simplex_iteration_count; return_status = @@ -4191,6 +4197,7 @@ HighsStatus Highs::callRunPostsolve(const HighsSolution& solution, "Solving the original LP from the solution after postsolve"); // Determine the timing record timer_.stop(timer_.solve_clock); + simplex_stats_ = this->ekk_instance_.getSimplexStats(); return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); // Recover the options From b2115a5e567fa09c03471d051778df8c3c236311 Mon Sep 17 00:00:00 2001 From: Julian Hall Date: Fri, 29 Nov 2024 13:44:05 +0000 Subject: [PATCH 19/40] Added invalidateSimplexStats() to Highs.h --- src/Highs.h | 6 +++++- src/lp_data/Highs.cpp | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Highs.h b/src/Highs.h index b2553f21c1..5f56941d61 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1507,7 +1507,8 @@ class Highs { // // Invalidates all solver data in Highs class members by calling // invalidateModelStatus(), invalidateSolution(), invalidateBasis(), - // invalidateInfo() and invalidateEkk() + // invalidateInfo(), invalidateEkk(), invalidateIis() and + // invalidateSimplexStats(); void invalidateUserSolverData(); // // Invalidates the model status, solution_ and info_ @@ -1534,6 +1535,9 @@ class Highs { // Invalidates iis_ void invalidateIis(); + // Invalidates simplex_stats_ and presolved_lp_simplex_stats_; + void invalidateSimplexStats(); + HighsStatus returnFromWriteSolution(FILE* file, const HighsStatus return_status); HighsStatus returnFromRun(const HighsStatus return_status, diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index f9119a9ba7..ec099dc261 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -3589,6 +3589,7 @@ void Highs::invalidateUserSolverData() { invalidateInfo(); invalidateEkk(); invalidateIis(); + invalidateSimplexStats(); } void Highs::invalidateModelStatusSolutionAndInfo() { @@ -3628,6 +3629,8 @@ void Highs::invalidateEkk() { ekk_instance_.invalidate(); } void Highs::invalidateIis() { iis_.invalidate(); } +void Highs::invalidateSimplexStats() { simplex_stats_.initialise(); presolved_lp_simplex_stats_.initialise(); } + HighsStatus Highs::completeSolutionFromDiscreteAssignment() { // Determine whether the current solution of a MIP is feasible and, // if not, try to assign values to continuous variables and discrete From 597b4e7be2af3d0c18a1136de2946c73d01dc296 Mon Sep 17 00:00:00 2001 From: Julian Hall Date: Fri, 29 Nov 2024 14:59:30 +0000 Subject: [PATCH 20/40] Added CSV reporting to SimplexStats --- src/Highs.h | 8 +++--- src/lp_data/HighsSolutionStats.h | 10 +++++++- src/simplex/HEkk.cpp | 44 ++++++++++++++++++++++---------- src/simplex/HEkk.h | 4 +-- 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/Highs.h b/src/Highs.h index 708d177f69..28650d66c2 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1220,14 +1220,14 @@ class Highs { return presolved_lp_simplex_stats_; } - void reportPresolvedLpSimplexStats(FILE* file) const { - presolved_lp_simplex_stats_.report(file, "Presolved LP"); + void reportPresolvedLpSimplexStats(FILE* file, const HighsInt style = HighsSimplexStatsReportPretty) const { + presolved_lp_simplex_stats_.report(file, style, "Presolved LP"); } const HighsSimplexStats& getSimplexStats() const { return simplex_stats_; } - void reportSimplexStats(FILE* file) const { - simplex_stats_.report(file, "Original LP"); + void reportSimplexStats(FILE* file, const HighsInt style = HighsSimplexStatsReportPretty) const { + simplex_stats_.report(file, style, "Original LP"); } void passSimplexStats(const HighsSimplexStats simplex_stats) { diff --git a/src/lp_data/HighsSolutionStats.h b/src/lp_data/HighsSolutionStats.h index ea43a9522a..bc4f3add5a 100644 --- a/src/lp_data/HighsSolutionStats.h +++ b/src/lp_data/HighsSolutionStats.h @@ -18,6 +18,12 @@ #include "lp_data/HConst.h" +enum HighsSimplexStatsReport { + HighsSimplexStatsReportPretty = 0, + HighsSimplexStatsReportCsvHeader, + HighsSimplexStatsReportCsvData +}; + struct HighsSimplexStats { bool valid; HighsInt iteration_count; @@ -28,7 +34,9 @@ struct HighsSimplexStats { double row_ep_density; double row_ap_density; double row_DSE_density; - void report(FILE* file, const std::string message = "") const; + void report(FILE* file, + const HighsInt style = HighsSimplexStatsReportPretty, + const std::string message = "") const; void initialise(const HighsInt iteration_count_ = 0); }; diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index ca743c1f98..91126abdf3 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -4425,19 +4425,37 @@ void HEkk::passSimplexStats(const HighsSimplexStats simplex_stats) { this->simplex_stats_ = simplex_stats; } -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, " 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::report(FILE* file, const HighsInt style, std::string message) const { + if (style == HighsSimplexStatsReportPretty) { + 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, " 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); + } else if (style == HighsSimplexStatsReportCsvHeader) { + fprintf(file, "valid,iteration_count,num_invert,last_invert_num_el,last_factored_basis_num_el,col_aq_density,row_ep_density,row_ap_density,row_DSE_density,"); + } else if (style == HighsSimplexStatsReportCsvData) { + fprintf(file, "%d,%d,%d,%d,%d,%g,%g,%g,%g,", + int(this->valid), + int(this->iteration_count), + int(this->num_invert), + int(this->last_invert_num_el), + int(this->last_factored_basis_num_el), + this->col_aq_density, + this->row_ep_density, + this->row_ap_density, + this->row_DSE_density); + } else { + fprintf(file, "Unknown simplex stats report style of %d\n", int(style)); + assert(123 == 456); + } } void HighsSimplexStats::initialise(const HighsInt iteration_count_) { diff --git a/src/simplex/HEkk.h b/src/simplex/HEkk.h index 62594dd822..a6ddcafc7f 100644 --- a/src/simplex/HEkk.h +++ b/src/simplex/HEkk.h @@ -160,8 +160,8 @@ class HEkk { const HighsSimplexStats& getSimplexStats() const { return simplex_stats_; } void passSimplexStats(const HighsSimplexStats simplex_stats); void initialiseSimplexStats() { simplex_stats_.initialise(iteration_count_); } - void reportSimplexStats(FILE* file, const std::string message = "") const { - simplex_stats_.report(file, message); + void reportSimplexStats(FILE* file, const HighsInt style = HighsSimplexStatsReportPretty, const std::string message = "") const { + simplex_stats_.report(file, style, message); } // Make this private later From 02d719c0733ef7d7b0984ea5b8ba0a12d35cd0ec Mon Sep 17 00:00:00 2001 From: Julian Hall Date: Fri, 29 Nov 2024 14:59:55 +0000 Subject: [PATCH 21/40] Added CSV reporting to SimplexStats; formatted --- src/Highs.h | 6 ++++-- src/lp_data/Highs.cpp | 5 ++++- src/lp_data/HighsSolutionStats.h | 5 ++--- src/simplex/HEkk.cpp | 33 ++++++++++++++++---------------- src/simplex/HEkk.h | 4 +++- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/Highs.h b/src/Highs.h index 28650d66c2..fbe6557d16 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1220,13 +1220,15 @@ class Highs { return presolved_lp_simplex_stats_; } - void reportPresolvedLpSimplexStats(FILE* file, const HighsInt style = HighsSimplexStatsReportPretty) const { + void reportPresolvedLpSimplexStats( + FILE* file, const HighsInt style = HighsSimplexStatsReportPretty) const { presolved_lp_simplex_stats_.report(file, style, "Presolved LP"); } const HighsSimplexStats& getSimplexStats() const { return simplex_stats_; } - void reportSimplexStats(FILE* file, const HighsInt style = HighsSimplexStatsReportPretty) const { + void reportSimplexStats( + FILE* file, const HighsInt style = HighsSimplexStatsReportPretty) const { simplex_stats_.report(file, style, "Original LP"); } diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index ec099dc261..d01875e1fe 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -3629,7 +3629,10 @@ void Highs::invalidateEkk() { ekk_instance_.invalidate(); } void Highs::invalidateIis() { iis_.invalidate(); } -void Highs::invalidateSimplexStats() { simplex_stats_.initialise(); presolved_lp_simplex_stats_.initialise(); } +void Highs::invalidateSimplexStats() { + simplex_stats_.initialise(); + presolved_lp_simplex_stats_.initialise(); +} HighsStatus Highs::completeSolutionFromDiscreteAssignment() { // Determine whether the current solution of a MIP is feasible and, diff --git a/src/lp_data/HighsSolutionStats.h b/src/lp_data/HighsSolutionStats.h index bc4f3add5a..94a8a5fd07 100644 --- a/src/lp_data/HighsSolutionStats.h +++ b/src/lp_data/HighsSolutionStats.h @@ -34,9 +34,8 @@ struct HighsSimplexStats { double row_ep_density; double row_ap_density; double row_DSE_density; - void report(FILE* file, - const HighsInt style = HighsSimplexStatsReportPretty, - const std::string message = "") const; + void report(FILE* file, const HighsInt style = HighsSimplexStatsReportPretty, + const std::string message = "") const; void initialise(const HighsInt iteration_count_ = 0); }; diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index 91126abdf3..3f4ee75fb3 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -4425,33 +4425,34 @@ void HEkk::passSimplexStats(const HighsSimplexStats simplex_stats) { this->simplex_stats_ = simplex_stats; } -void HighsSimplexStats::report(FILE* file, const HighsInt style, std::string message) const { +void HighsSimplexStats::report(FILE* file, const HighsInt style, + std::string message) const { if (style == HighsSimplexStatsReportPretty) { 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, " 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); + this->last_invert_num_el); fprintf(file, " last_factored_basis_num_el = %d\n", - this->last_factored_basis_num_el); + 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); + fprintf(file, " row_DSE_density = %g\n", + this->row_DSE_density); } else if (style == HighsSimplexStatsReportCsvHeader) { - fprintf(file, "valid,iteration_count,num_invert,last_invert_num_el,last_factored_basis_num_el,col_aq_density,row_ep_density,row_ap_density,row_DSE_density,"); + fprintf(file, + "valid,iteration_count,num_invert,last_invert_num_el,last_factored_" + "basis_num_el,col_aq_density,row_ep_density,row_ap_density,row_DSE_" + "density,"); } else if (style == HighsSimplexStatsReportCsvData) { - fprintf(file, "%d,%d,%d,%d,%d,%g,%g,%g,%g,", - int(this->valid), - int(this->iteration_count), - int(this->num_invert), - int(this->last_invert_num_el), - int(this->last_factored_basis_num_el), - this->col_aq_density, - this->row_ep_density, - this->row_ap_density, - this->row_DSE_density); + fprintf(file, "%d,%d,%d,%d,%d,%g,%g,%g,%g,", int(this->valid), + int(this->iteration_count), int(this->num_invert), + int(this->last_invert_num_el), + int(this->last_factored_basis_num_el), this->col_aq_density, + this->row_ep_density, this->row_ap_density, this->row_DSE_density); } else { fprintf(file, "Unknown simplex stats report style of %d\n", int(style)); assert(123 == 456); diff --git a/src/simplex/HEkk.h b/src/simplex/HEkk.h index a6ddcafc7f..e298ee5212 100644 --- a/src/simplex/HEkk.h +++ b/src/simplex/HEkk.h @@ -160,7 +160,9 @@ class HEkk { const HighsSimplexStats& getSimplexStats() const { return simplex_stats_; } void passSimplexStats(const HighsSimplexStats simplex_stats); void initialiseSimplexStats() { simplex_stats_.initialise(iteration_count_); } - void reportSimplexStats(FILE* file, const HighsInt style = HighsSimplexStatsReportPretty, const std::string message = "") const { + void reportSimplexStats(FILE* file, + const HighsInt style = HighsSimplexStatsReportPretty, + const std::string message = "") const { simplex_stats_.report(file, style, message); } From 59d8c02f8ae71e0ad29dc738ffd8ebb8106f09c9 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Fri, 29 Nov 2024 22:11:51 +0000 Subject: [PATCH 22/40] Root relaxation solve needs debugging --- src/ipm/ipx/lp_solver.cc | 6 +++-- src/lp_data/Highs.cpp | 2 ++ src/mip/HighsMipSolver.cpp | 46 ++++++++++++++++++++++++++++------ src/mip/HighsMipSolver.h | 8 +++--- src/mip/HighsMipSolverData.cpp | 10 +++----- src/mip/HighsMipSolverData.h | 1 + src/mip/MipTimer.h | 3 +++ 7 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index 0d422e8c65..398a8764d3 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -490,13 +490,15 @@ void LpSolver::RunInitialIPM(IPM& ipm) { // min(control_.cr1_maxiter(), 500,10+m/20) // // iterations, ignoring control_.cr1_maxiter() if negative + // + // 2049 revert all this Int m = model_.rows(); ipxint df_cr1_maxiter = std::min(500l, (long) (10+m/20)); ipxint cr1_maxiter = df_cr1_maxiter; ipxint control_cr1_maxiter = control_.cr1_maxiter(); if (control_cr1_maxiter > 0) cr1_maxiter = std::min(control_cr1_maxiter, cr1_maxiter); - printf("LpSolver::RunInitialIPM Using kkt.maxiter = %d, from df_cr1_maxiter = %d and control_cr1_maxiter = %d\n", - int(cr1_maxiter), int(df_cr1_maxiter), int(control_cr1_maxiter)); + // printf("LpSolver::RunInitialIPM Using kkt.maxiter = %d, from df_cr1_maxiter = %d and control_cr1_maxiter = %d\n", + // int(cr1_maxiter), int(df_cr1_maxiter), int(control_cr1_maxiter)); kkt.maxiter(cr1_maxiter); ipm.maxiter(control_.ipm_maxiter()); } else { diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index d01875e1fe..004366372f 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -1320,6 +1320,7 @@ HighsStatus Highs::solve() { solveLp(incumbent_lp, "Not presolved: solving the LP", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); + presolved_lp_simplex_stats_ = simplex_stats_; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); if (return_status == HighsStatus::kError) @@ -1333,6 +1334,7 @@ HighsStatus Highs::solve() { solveLp(incumbent_lp, "Problem not reduced by presolve: solving the LP", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); + presolved_lp_simplex_stats_ = simplex_stats_; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); if (return_status == HighsStatus::kError) diff --git a/src/mip/HighsMipSolver.cpp b/src/mip/HighsMipSolver.cpp index 6585c0ddb5..3d975cf7f6 100644 --- a/src/mip/HighsMipSolver.cpp +++ b/src/mip/HighsMipSolver.cpp @@ -134,7 +134,7 @@ void HighsMipSolver::run() { mipdata_->runPresolve(options_mip_->presolve_reduction_limit); analysis_.mipTimerStop(kMipClockRunPresolve); analysis_.mipTimerStop(kMipClockPresolve); - if (analysis_.analyse_mip_time & !submip) + if (analysis_.analyse_mip_time && !submip) highsLogUser(options_mip_->log_options, HighsLogType::kInfo, "MIP-Timing: %11.2g - completed presolve\n", timer_.read()); // Identify whether time limit has been reached (in presolve) @@ -158,16 +158,48 @@ void HighsMipSolver::run() { analysis_.mipTimerStart(kMipClockSolve); - if (analysis_.analyse_mip_time & !submip) + if (analysis_.analyse_mip_time && !submip) highsLogUser(options_mip_->log_options, HighsLogType::kInfo, "MIP-Timing: %11.2g - starting setup\n", timer_.read()); analysis_.mipTimerStart(kMipClockRunSetup); mipdata_->runSetup(); analysis_.mipTimerStop(kMipClockRunSetup); - if (analysis_.analyse_mip_time & !submip) + if (analysis_.analyse_mip_time && !submip) highsLogUser(options_mip_->log_options, HighsLogType::kInfo, - "MIP-Timing: %11.2g - completed setup\n", - timer_.read(timer_.total_clock)); + "MIP-Timing: %11.2g - completed setup\n", + timer_.read(timer_.total_clock)); + if (!submip) { + if (analysis_.analyse_mip_time) { + highsLogUser(options_mip_->log_options, HighsLogType::kInfo, + "MIP-Timing: %11.2g - starting relaxation simplex solve\n", timer_.read()); + analysis_.mipTimerStart(kMipClockRelaxationSimplexSolve); + analysis_.mipTimerStart(kMipClockSimplexNoBasisSolveLp); + } + Highs root; + HighsLp lpmodel(*model_); + root.passModel(std::move(lpmodel)); + root.setOptionValue("solve_relaxation", true); + root.setOptionValue("output_flag", true); + HighsStatus status = root.run(); + modelstatus_ = root.getModelStatus(); + if (status != HighsStatus::kOk) { + cleanupSolve(); + return; + } + if (modelstatus_ != HighsModelStatus::kOptimal) { + cleanupSolve(); + return; + } + mipdata_->firstrootbasis = root.getBasis(); + mipdata_->simplex_stats = root.getSimplexStats(); + mipdata_->simplex_stats.report(stdout, HighsSimplexStatsReportPretty, "Root node"); + if (analysis_.analyse_mip_time) { + analysis_.mipTimerStop(kMipClockSimplexNoBasisSolveLp); + analysis_.mipTimerStop(kMipClockRelaxationSimplexSolve); + highsLogUser(options_mip_->log_options, HighsLogType::kInfo, + "MIP-Timing: %11.2g - completed relaxation simplex solve\n", timer_.read()); + } + } restart: if (modelstatus_ == HighsModelStatus::kNotset) { // Check limits have not been reached before evaluating root node @@ -186,7 +218,7 @@ void HighsMipSolver::run() { return; } analysis_.mipTimerStop(kMipClockTrivialHeuristics); - if (analysis_.analyse_mip_time & !submip) + if (analysis_.analyse_mip_time && !submip) highsLogUser(options_mip_->log_options, HighsLogType::kInfo, "MIP-Timing: %11.2g - starting evaluate root node\n", timer_.read(timer_.total_clock)); @@ -198,7 +230,7 @@ void HighsMipSolver::run() { if (analysis_.analyse_mip_time && analysis_.mipTimerRunning(kMipClockIpmSolveLp)) analysis_.mipTimerStop(kMipClockIpmSolveLp); - if (analysis_.analyse_mip_time & !submip) + if (analysis_.analyse_mip_time && !submip) highsLogUser(options_mip_->log_options, HighsLogType::kInfo, "MIP-Timing: %11.2g - completed evaluate root node\n", timer_.read(timer_.total_clock)); diff --git a/src/mip/HighsMipSolver.h b/src/mip/HighsMipSolver.h index 0a1eaae0ca..0a7a3395c6 100644 --- a/src/mip/HighsMipSolver.h +++ b/src/mip/HighsMipSolver.h @@ -92,10 +92,10 @@ class HighsMipSolver { ~HighsMipSolver(); - void setModel(const HighsLp& model) { - model_ = &model; - solution_objective_ = kHighsInf; - } + // void setModel(const HighsLp& model) { + // model_ = &model; + // solution_objective_ = kHighsInf; + // } mutable HighsTimer timer_; void cleanupSolve(); diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index f03114f2ab..ff5404f88f 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -304,7 +304,7 @@ void HighsMipSolverData::startAnalyticCenterComputation( // due to early return in the root node evaluation assert(mipsolver.options_mip_->mip_compute_analytic_centre >= kMipAnalyticCentreCalulationOriginal); - const bool ac_logging = !mipsolver.submip; + const bool ac_logging = false;// !mipsolver.submip; // 2049 unset this Highs ipm; if (mipsolver.options_mip_->mip_compute_analytic_centre == kMipAnalyticCentreCalulationOriginal) { @@ -318,8 +318,7 @@ void HighsMipSolverData::startAnalyticCenterComputation( } ipm.setOptionValue("presolve", "off"); ipm.setOptionValue("output_flag", false); - ipm.setOptionValue("output_flag", - !mipsolver.submip); // 2049 unset this ultimately + // ipm.setOptionValue("output_flag", !mipsolver.submip); // 2049 unset this ultimately ipm.setOptionValue("ipm_iteration_limit", 200); double time_available = std::max(mipsolver.options_mip_->time_limit - @@ -348,14 +347,13 @@ void HighsMipSolverData::startAnalyticCenterComputation( // ipm.setOptionValue("cr2_iteration_limit", cr2_iteration_limit); HighsSimplexStats simplex_stats = lp.getLpSolver().getSimplexStats(); - if (!mipsolver.submip) simplex_stats.report(stdout); + // if (!mipsolver.submip) simplex_stats.report(stdout); ipm.passSimplexStats(simplex_stats); HighsLp lpmodel(*mipsolver.model_); lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); ipm.passModel(std::move(lpmodel)); - if (!mipsolver.submip) - ipm.writeModel(mipsolver.model_->model_name_ + "_ipm.mps"); + // if (!mipsolver.submip)ipm.writeModel(mipsolver.model_->model_name_ + "_ipm.mps"); if (ac_logging) num_analytic_centre_start++; double tt0 = mipsolver.timer_.read(mipsolver.timer_.total_clock); diff --git a/src/mip/HighsMipSolverData.h b/src/mip/HighsMipSolverData.h index ba8b8c3323..aa95cf3ac4 100644 --- a/src/mip/HighsMipSolverData.h +++ b/src/mip/HighsMipSolverData.h @@ -91,6 +91,7 @@ struct HighsMipSolverData { HighsInt numRestartsRoot; HighsInt numCliqueEntriesAfterPresolve; HighsInt numCliqueEntriesAfterFirstPresolve; + HighsSimplexStats simplex_stats; std::vector ARstart_; std::vector ARindex_; diff --git a/src/mip/MipTimer.h b/src/mip/MipTimer.h index bc6534cd93..7f1d6476c3 100644 --- a/src/mip/MipTimer.h +++ b/src/mip/MipTimer.h @@ -24,6 +24,7 @@ enum iClockMip { kMipClockInit, kMipClockRunPresolve, kMipClockRunSetup, + kMipClockRelaxationSimplexSolve, kMipClockTrivialHeuristics, kMipClockEvaluateRootNode, kMipClockPerformAging0, @@ -96,6 +97,7 @@ class MipTimer { clock[kMipClockInit] = timer_pointer->clock_def("Initialise"); clock[kMipClockRunPresolve] = timer_pointer->clock_def("Run presolve"); clock[kMipClockRunSetup] = timer_pointer->clock_def("Run setup"); + clock[kMipClockRelaxationSimplexSolve] = timer_pointer->clock_def("Relaxation simplex solve"); clock[kMipClockTrivialHeuristics] = timer_pointer->clock_def("Trivial heuristics"); clock[kMipClockEvaluateRootNode] = @@ -255,6 +257,7 @@ class MipTimer { const std::vector mip_clock_list{kMipClockInit, kMipClockRunPresolve, kMipClockRunSetup, + kMipClockRelaxationSimplexSolve, kMipClockTrivialHeuristics, kMipClockEvaluateRootNode, kMipClockPerformAging0, From a6aacf8f4a8f89a651e3e08ae2f79c33df158076 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Sat, 30 Nov 2024 10:54:08 +0000 Subject: [PATCH 23/40] HighsSolutionStats.h is now HighsSolverStats.h --- cmake/sources-python.cmake | 2 +- cmake/sources.cmake | 2 +- src/Highs.h | 8 +++--- src/ipm/ipx/control.h | 3 ++- src/ipm/ipx/ipm.cc | 7 ++++++ src/ipm/ipx/ipx_parameters.h | 2 +- src/lp_data/HighsSolutionStats.h | 42 -------------------------------- src/mip/HighsMipSolver.cpp | 10 +++++--- src/mip/HighsMipSolverData.cpp | 11 ++++----- src/simplex/HEkk.cpp | 9 +++---- src/simplex/HEkk.h | 8 +++--- 11 files changed, 35 insertions(+), 69 deletions(-) delete mode 100644 src/lp_data/HighsSolutionStats.h diff --git a/cmake/sources-python.cmake b/cmake/sources-python.cmake index b6d3349a3e..5c437f8e42 100644 --- a/cmake/sources-python.cmake +++ b/cmake/sources-python.cmake @@ -311,8 +311,8 @@ set(highs_headers_python src/lp_data/HighsRanging.h src/lp_data/HighsSolution.h src/lp_data/HighsSolutionDebug.h - src/lp_data/HighsSolutionStats.h src/lp_data/HighsSolve.h + src/lp_data/HighsSolverStats.h src/lp_data/HighsStatus.h src/lp_data/HStruct.h src/mip/HighsCliqueTable.h diff --git a/cmake/sources.cmake b/cmake/sources.cmake index 7335a2e83d..4e71dc176c 100644 --- a/cmake/sources.cmake +++ b/cmake/sources.cmake @@ -315,8 +315,8 @@ set(highs_headers lp_data/HighsRanging.h lp_data/HighsSolution.h lp_data/HighsSolutionDebug.h - lp_data/HighsSolutionStats.h lp_data/HighsSolve.h + lp_data/HighsSolverStats.h lp_data/HighsStatus.h lp_data/HStruct.h mip/HighsCliqueTable.h diff --git a/src/Highs.h b/src/Highs.h index fbe6557d16..00620b98a5 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1221,15 +1221,15 @@ class Highs { } void reportPresolvedLpSimplexStats( - FILE* file, const HighsInt style = HighsSimplexStatsReportPretty) const { - presolved_lp_simplex_stats_.report(file, style, "Presolved LP"); + FILE* file, const HighsInt style = HighsSolverStatsReportPretty) const { + presolved_lp_simplex_stats_.report(file, "Presolved LP", style); } const HighsSimplexStats& getSimplexStats() const { return simplex_stats_; } void reportSimplexStats( - FILE* file, const HighsInt style = HighsSimplexStatsReportPretty) const { - simplex_stats_.report(file, style, "Original LP"); + FILE* file, const HighsInt style = HighsSolverStatsReportPretty) const { + simplex_stats_.report(file, "Original LP", style); } void passSimplexStats(const HighsSimplexStats simplex_stats) { diff --git a/src/ipm/ipx/control.h b/src/ipm/ipx/control.h index c5b19969e0..482677cf19 100644 --- a/src/ipm/ipx/control.h +++ b/src/ipm/ipx/control.h @@ -94,8 +94,9 @@ class Control { double centringRatioReduction() const {return parameters_.centring_ratio_reduction; } double centringAlphaScaling() const{return parameters_.centring_alpha_scaling; } ipxint badProductsTolerance() const{return parameters_.bad_products_tolerance; } + HighsSimplexStats simplexStats() const{return parameters_.simplex_stats; } - const Parameters& parameters() const; + const Parameters& parameters() const; void parameters(const Parameters& new_parameters); void callback(HighsCallback* callback); diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index b67aa27193..a2ff8e9aaa 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -103,6 +103,13 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { kkt_->iterSum() << ", max = " << kkt_->iterMax() << ")\n"; control_.hLog(h_logging_stream); + // Find nnz for L and U + // + // incopororate kkt_->current_fill() * + double ipm_cost_measure = kkt_->iterSum(); + HighsSimplexStats simplex_stats = control_.simplexStats(); + simplex_stats.report(stdout, "In ipm.cpp"); + double simplex_cost_measure = simplex_stats.iteration_count; // info->iter++; PrintOutput(); diff --git a/src/ipm/ipx/ipx_parameters.h b/src/ipm/ipx/ipx_parameters.h index 1fc4af238a..c19ddea438 100644 --- a/src/ipm/ipx/ipx_parameters.h +++ b/src/ipm/ipx/ipx_parameters.h @@ -2,7 +2,7 @@ #define IPX_PARAMETERS_H_ #include "io/HighsIO.h" -#include "lp_data/HighsSolutionStats.h" +#include "lp_data/HighsSolverStats.h" #include "ipm/ipx/ipx_config.h" #include diff --git a/src/lp_data/HighsSolutionStats.h b/src/lp_data/HighsSolutionStats.h deleted file mode 100644 index 94a8a5fd07..0000000000 --- a/src/lp_data/HighsSolutionStats.h +++ /dev/null @@ -1,42 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -/* */ -/* This file is part of the HiGHS linear optimization suite */ -/* */ -/* Written and engineered 2008-2024 by Julian Hall, Ivet Galabova, */ -/* Leona Gottwald and Michael Feldmeier */ -/* */ -/* Available as open-source under the MIT License */ -/* */ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -/**@file lp_data/HighsSolutionStats.h - * @brief Structs for HiGHS - */ -#ifndef LP_DATA_HIGHSSOLUTIONSTATS_H_ -#define LP_DATA_HIGHSSOLUTIONSTATS_H_ - -#include - -#include "lp_data/HConst.h" - -enum HighsSimplexStatsReport { - HighsSimplexStatsReportPretty = 0, - HighsSimplexStatsReportCsvHeader, - HighsSimplexStatsReportCsvData -}; - -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 HighsInt style = HighsSimplexStatsReportPretty, - const std::string message = "") const; - void initialise(const HighsInt iteration_count_ = 0); -}; - -#endif /* LP_DATA_HIGHSSOLUTIONSTATS_H_ */ diff --git a/src/mip/HighsMipSolver.cpp b/src/mip/HighsMipSolver.cpp index 3d975cf7f6..5bd5601a45 100644 --- a/src/mip/HighsMipSolver.cpp +++ b/src/mip/HighsMipSolver.cpp @@ -177,22 +177,24 @@ void HighsMipSolver::run() { } Highs root; HighsLp lpmodel(*model_); + lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); root.passModel(std::move(lpmodel)); root.setOptionValue("solve_relaxation", true); root.setOptionValue("output_flag", true); HighsStatus status = root.run(); - modelstatus_ = root.getModelStatus(); + HighsModelStatus root_modelstatus = root.getModelStatus(); if (status != HighsStatus::kOk) { cleanupSolve(); return; } - if (modelstatus_ != HighsModelStatus::kOptimal) { + if (root_modelstatus != HighsModelStatus::kOptimal) { + modelstatus_ = root_modelstatus; cleanupSolve(); return; } mipdata_->firstrootbasis = root.getBasis(); - mipdata_->simplex_stats = root.getSimplexStats(); - mipdata_->simplex_stats.report(stdout, HighsSimplexStatsReportPretty, "Root node"); + mipdata_->simplex_stats = root.getPresolvedLpSimplexStats(); + mipdata_->simplex_stats.report(stdout, "Root node"); if (analysis_.analyse_mip_time) { analysis_.mipTimerStop(kMipClockSimplexNoBasisSolveLp); analysis_.mipTimerStop(kMipClockRelaxationSimplexSolve); diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index ff5404f88f..59377683d6 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -304,7 +304,7 @@ void HighsMipSolverData::startAnalyticCenterComputation( // due to early return in the root node evaluation assert(mipsolver.options_mip_->mip_compute_analytic_centre >= kMipAnalyticCentreCalulationOriginal); - const bool ac_logging = false;// !mipsolver.submip; // 2049 unset this + const bool ac_logging = !mipsolver.submip; // 2049 unset this Highs ipm; if (mipsolver.options_mip_->mip_compute_analytic_centre == kMipAnalyticCentreCalulationOriginal) { @@ -318,14 +318,14 @@ void HighsMipSolverData::startAnalyticCenterComputation( } ipm.setOptionValue("presolve", "off"); ipm.setOptionValue("output_flag", false); - // ipm.setOptionValue("output_flag", !mipsolver.submip); // 2049 unset this ultimately + ipm.setOptionValue("output_flag", !mipsolver.submip); // 2049 unset this ultimately ipm.setOptionValue("ipm_iteration_limit", 200); double time_available = std::max(mipsolver.options_mip_->time_limit - mipsolver.timer_.read(mipsolver.timer_.total_clock), 0.1); ipm.setOptionValue("time_limit", time_available); - // ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this + ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this // ultimately // // cr1_iteration_limit is what's set internal to IPX to limit the @@ -346,9 +346,8 @@ void HighsMipSolverData::startAnalyticCenterComputation( // 2049 Set this ultimately // ipm.setOptionValue("cr2_iteration_limit", cr2_iteration_limit); - HighsSimplexStats simplex_stats = lp.getLpSolver().getSimplexStats(); - // if (!mipsolver.submip) simplex_stats.report(stdout); - ipm.passSimplexStats(simplex_stats); + if (!mipsolver.submip) this->simplex_stats.report(stdout); + ipm.passSimplexStats(this->simplex_stats); HighsLp lpmodel(*mipsolver.model_); lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); ipm.passModel(std::move(lpmodel)); diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index 3f4ee75fb3..44b13c7e80 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -4425,9 +4425,8 @@ void HEkk::passSimplexStats(const HighsSimplexStats simplex_stats) { this->simplex_stats_ = simplex_stats; } -void HighsSimplexStats::report(FILE* file, const HighsInt style, - std::string message) const { - if (style == HighsSimplexStatsReportPretty) { +void HighsSimplexStats::report(FILE* file, std::string message, const HighsInt style) const { + if (style == HighsSolverStatsReportPretty) { fprintf(file, "\nSimplex stats: %s\n", message.c_str()); fprintf(file, " valid = %d\n", this->valid); fprintf(file, " iteration_count = %d\n", @@ -4442,12 +4441,12 @@ void HighsSimplexStats::report(FILE* file, const HighsInt style, fprintf(file, " row_ap_density = %g\n", this->row_ap_density); fprintf(file, " row_DSE_density = %g\n", this->row_DSE_density); - } else if (style == HighsSimplexStatsReportCsvHeader) { + } else if (style == HighsSolverStatsReportCsvHeader) { fprintf(file, "valid,iteration_count,num_invert,last_invert_num_el,last_factored_" "basis_num_el,col_aq_density,row_ep_density,row_ap_density,row_DSE_" "density,"); - } else if (style == HighsSimplexStatsReportCsvData) { + } else if (style == HighsSolverStatsReportCsvData) { fprintf(file, "%d,%d,%d,%d,%d,%g,%g,%g,%g,", int(this->valid), int(this->iteration_count), int(this->num_invert), int(this->last_invert_num_el), diff --git a/src/simplex/HEkk.h b/src/simplex/HEkk.h index e298ee5212..efbd20c515 100644 --- a/src/simplex/HEkk.h +++ b/src/simplex/HEkk.h @@ -15,7 +15,7 @@ #define SIMPLEX_HEKK_H_ #include "lp_data/HighsCallback.h" -#include "lp_data/HighsSolutionStats.h" +#include "lp_data/HighsSolverStats.h" #include "simplex/HSimplexNla.h" #include "simplex/HighsSimplexAnalysis.h" #include "util/HSet.h" @@ -161,9 +161,9 @@ class HEkk { void passSimplexStats(const HighsSimplexStats simplex_stats); void initialiseSimplexStats() { simplex_stats_.initialise(iteration_count_); } void reportSimplexStats(FILE* file, - const HighsInt style = HighsSimplexStatsReportPretty, - const std::string message = "") const { - simplex_stats_.report(file, style, message); + const std::string message = "", + const HighsInt style = HighsSolverStatsReportPretty) const { + simplex_stats_.report(file, message, style); } // Make this private later From 5b332f654f5d186ad0b3b189ff12c4c0bdd21b49 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Sat, 30 Nov 2024 18:25:13 +0000 Subject: [PATCH 24/40] Have extracted matrix_nz from depths of basic_lu! --- check/TestLpSolvers.cpp | 9 +++++ src/ipm/ipx/basiclu_wrapper.cc | 4 +++ src/ipm/ipx/basiclu_wrapper.h | 1 + src/ipm/ipx/basis.cc | 6 ++++ src/ipm/ipx/basis.h | 2 ++ src/ipm/ipx/forrest_tomlin.cc | 2 ++ src/ipm/ipx/forrest_tomlin.h | 1 + src/ipm/ipx/lu_update.cc | 2 ++ src/ipm/ipx/lu_update.h | 4 +++ src/lp_data/HighsSolverStats.h | 62 ++++++++++++++++++++++++++++++++++ src/simplex/HEkk.cpp | 32 ++++++++++++++++++ 11 files changed, 125 insertions(+) create mode 100644 src/lp_data/HighsSolverStats.h diff --git a/check/TestLpSolvers.cpp b/check/TestLpSolvers.cpp index 6b7d5d1c8c..c22bd26868 100644 --- a/check/TestLpSolvers.cpp +++ b/check/TestLpSolvers.cpp @@ -659,6 +659,9 @@ TEST_CASE("simplex-stats", "[highs_lp_solver]") { REQUIRE(h.run() == HighsStatus::kOk); REQUIRE(simplex_stats.valid); + REQUIRE(simplex_stats.num_col > 0); + REQUIRE(simplex_stats.num_row > 0); + REQUIRE(simplex_stats.num_nz > 0); REQUIRE(simplex_stats.iteration_count >= 0); REQUIRE(simplex_stats.num_invert > 0); REQUIRE(simplex_stats.last_invert_num_el > 0); @@ -677,6 +680,9 @@ TEST_CASE("simplex-stats", "[highs_lp_solver]") { if (dev_run) h.reportSimplexStats(stdout); REQUIRE(presolved_lp_simplex_stats.valid); + REQUIRE(presolved_lp_simplex_stats.num_col > 0); + REQUIRE(presolved_lp_simplex_stats.num_row > 0); + REQUIRE(presolved_lp_simplex_stats.num_nz > 0); REQUIRE(presolved_lp_simplex_stats.iteration_count > 0); REQUIRE(presolved_lp_simplex_stats.num_invert > 0); REQUIRE(presolved_lp_simplex_stats.last_invert_num_el > 0); @@ -691,6 +697,9 @@ TEST_CASE("simplex-stats", "[highs_lp_solver]") { h.setOptionValue("presolve", kHighsOffString); REQUIRE(h.run() == HighsStatus::kOk); REQUIRE(simplex_stats.valid); + REQUIRE(simplex_stats.num_col > 0); + REQUIRE(simplex_stats.num_row > 0); + REQUIRE(simplex_stats.num_nz > 0); REQUIRE(simplex_stats.iteration_count > 0); REQUIRE(simplex_stats.num_invert > 0); REQUIRE(simplex_stats.last_invert_num_el > 0); diff --git a/src/ipm/ipx/basiclu_wrapper.cc b/src/ipm/ipx/basiclu_wrapper.cc index e38686f88c..b22697ec34 100644 --- a/src/ipm/ipx/basiclu_wrapper.cc +++ b/src/ipm/ipx/basiclu_wrapper.cc @@ -253,6 +253,10 @@ bool BasicLu::_NeedFreshFactorization() { return nforrest == dim || update_cost > 1.0; } +Int BasicLu::_matrix_nz() const { + return xstore_[BASICLU_MATRIX_NZ]; +} + double BasicLu::_fill_factor() const { return fill_factor_; } diff --git a/src/ipm/ipx/basiclu_wrapper.h b/src/ipm/ipx/basiclu_wrapper.h index 6ad9c5775d..669fa7b9d1 100644 --- a/src/ipm/ipx/basiclu_wrapper.h +++ b/src/ipm/ipx/basiclu_wrapper.h @@ -24,6 +24,7 @@ class BasicLu : public LuUpdate { void _BtranForUpdate(Int j, IndexedVector& lhs) override; Int _Update(double pivot) override; bool _NeedFreshFactorization() override; + Int _matrix_nz() const override; double _fill_factor() const override; double _pivottol() const override; void _pivottol(double new_pivottol) override; diff --git a/src/ipm/ipx/basis.cc b/src/ipm/ipx/basis.cc index 994335163a..823873b50c 100644 --- a/src/ipm/ipx/basis.cc +++ b/src/ipm/ipx/basis.cc @@ -130,6 +130,7 @@ Int Basis::Factorize() { Int flag = lu_->Factorize(begin.data(), end.data(), AI.rowidx(), AI.values(), false); num_factorizations_++; + matrix_nz_ = lu_->matrix_nz(); fill_factors_.push_back(lu_->fill_factor()); if (flag & 2) { AdaptToSingularFactorization(); @@ -449,6 +450,10 @@ double Basis::time_update() const { return time_update_; } +Int Basis::matrix_nz() const { + return matrix_nz_; +} + double Basis::current_fill() const { if (fill_factors_.empty()) return 0.0; @@ -637,6 +642,7 @@ void Basis::CrashFactorize(Int* num_dropped) { Int flag = lu_->Factorize(begin.data(), end.data(), AI.rowidx(), AI.values(), true); num_factorizations_++; + matrix_nz_ = lu_->matrix_nz(); fill_factors_.push_back(lu_->fill_factor()); Int ndropped = 0; if (flag & 2) diff --git a/src/ipm/ipx/basis.h b/src/ipm/ipx/basis.h index 19c8a6adca..e8f7e2df57 100644 --- a/src/ipm/ipx/basis.h +++ b/src/ipm/ipx/basis.h @@ -219,6 +219,7 @@ class Basis { double time_ftran() const; // time FTRAN, including partial double time_btran() const; // time BTRAN, including partial double time_update() const; // time LU update + Int matrix_nz() const; // Current nonzeros of matrix to be factored double current_fill() const; // Current LU fill factors double mean_fill() const; // geom. mean of LU fill factors double max_fill() const; // max LU fill factor @@ -308,6 +309,7 @@ class Basis { double time_btran_{0.0}; // time for BTRAN ops, including partial double time_update_{0.0}; // time for LU updates double time_factorize_{0.0}; // time for LU factorizations + Int matrix_nz_{0}; // nonzeros of matrix to be factored std::vector fill_factors_; // fill factors from LU factorizations double sum_ftran_density_{0.0}; double sum_btran_density_{0.0}; diff --git a/src/ipm/ipx/forrest_tomlin.cc b/src/ipm/ipx/forrest_tomlin.cc index 96004f8664..d519db2de9 100644 --- a/src/ipm/ipx/forrest_tomlin.cc +++ b/src/ipm/ipx/forrest_tomlin.cc @@ -235,6 +235,8 @@ bool ForrestTomlin::_NeedFreshFactorization() { return false; } +Int ForrestTomlin::_matrix_nz() const { return 0;} + double ForrestTomlin::_fill_factor() const { return fill_factor_; } diff --git a/src/ipm/ipx/forrest_tomlin.h b/src/ipm/ipx/forrest_tomlin.h index dc60bfd6ab..4989b2d127 100644 --- a/src/ipm/ipx/forrest_tomlin.h +++ b/src/ipm/ipx/forrest_tomlin.h @@ -44,6 +44,7 @@ class ForrestTomlin : public LuUpdate { void _BtranForUpdate(Int j, IndexedVector& lhs) override; Int _Update(double pivot) override; bool _NeedFreshFactorization() override; + Int _matrix_nz() const override; double _fill_factor() const override; double _pivottol() const override; void _pivottol(double new_pivottol) override; diff --git a/src/ipm/ipx/lu_update.cc b/src/ipm/ipx/lu_update.cc index bd2d264319..cb22df744e 100644 --- a/src/ipm/ipx/lu_update.cc +++ b/src/ipm/ipx/lu_update.cc @@ -43,6 +43,8 @@ bool LuUpdate::NeedFreshFactorization() { return _NeedFreshFactorization(); } +Int LuUpdate::matrix_nz() const { return 0; } + double LuUpdate::fill_factor() const { return _fill_factor(); } diff --git a/src/ipm/ipx/lu_update.h b/src/ipm/ipx/lu_update.h index 299ec4d8c0..de632c729b 100644 --- a/src/ipm/ipx/lu_update.h +++ b/src/ipm/ipx/lu_update.h @@ -94,6 +94,9 @@ class LuUpdate { // or speed. No stability test is done. bool NeedFreshFactorization(); + // Returns nnz(B) from the last factorization. + Int matrix_nz() const; + // Returns (nnz(L)+nnz(U))/nnz(B) from the last factorization. double fill_factor() const; @@ -117,6 +120,7 @@ class LuUpdate { virtual void _BtranForUpdate(Int p, IndexedVector& lhs) = 0; virtual Int _Update(double pivot) = 0; virtual bool _NeedFreshFactorization() = 0; + virtual Int _matrix_nz() const = 0; virtual double _fill_factor() const = 0; virtual double _pivottol() const = 0; virtual void _pivottol(double new_pivottol) = 0; diff --git a/src/lp_data/HighsSolverStats.h b/src/lp_data/HighsSolverStats.h new file mode 100644 index 0000000000..ffa8323dd1 --- /dev/null +++ b/src/lp_data/HighsSolverStats.h @@ -0,0 +1,62 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* This file is part of the HiGHS linear optimization suite */ +/* */ +/* Written and engineered 2008-2024 by Julian Hall, Ivet Galabova, */ +/* Leona Gottwald and Michael Feldmeier */ +/* */ +/* Available as open-source under the MIT License */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/**@file lp_data/HighsSolverStats.h + * @brief Structs for HiGHS + */ +#ifndef LP_DATA_HIGHSSOLVERSTATS_H_ +#define LP_DATA_HIGHSSOLVERSTATS_H_ + +#include + +#include "lp_data/HConst.h" + +enum HighsSolverStatsReport { + HighsSolverStatsReportPretty = 0, + HighsSolverStatsReportCsvHeader, + HighsSolverStatsReportCsvData +}; + +struct HighsSimplexStats { + bool valid; + HighsInt num_col; + HighsInt num_row; + HighsInt num_nz; + 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; + double workEstimate(); + void report(FILE* file, + const std::string message = "", + const HighsInt style = HighsSolverStatsReportPretty) const; + void initialise(const HighsInt iteration_count_ = 0); +}; + +struct HighsIpxStats { + bool valid; + HighsInt num_col; + HighsInt num_row; + HighsInt num_nz; + HighsInt ipm_iteration_count; + HighsInt average_cr_count; + HighsInt last_invert_num_el; + HighsInt last_factored_basis_num_el; + double workEstimate(); + void report(FILE* file, const std::string message = "", + const HighsInt style = HighsSolverStatsReportPretty) const; + void initialise(); +}; + +#endif /* LP_DATA_HIGHSSOLVERSTATS_H_ */ diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index 44b13c7e80..a3dddcf51f 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -3503,6 +3503,10 @@ HighsStatus HEkk::returnFromEkkSolve(const HighsStatus return_status) { // reverts to its value given by options_ if (analysis_.analyse_simplex_time) analysis_.reportSimplexTimer(); simplex_stats_.valid = true; + simplex_stats_.num_col = lp_.num_col_; + simplex_stats_.num_row = lp_.num_row_; + simplex_stats_.num_nz = lp_.a_matrix_.numNz(); + // Since HEkk::iteration_count_ includes iteration on presolved LP, // simplex_stats_.iteration_count is initialised to - // HEkk::iteration_count_ @@ -4429,6 +4433,12 @@ void HighsSimplexStats::report(FILE* file, std::string message, const HighsInt s if (style == HighsSolverStatsReportPretty) { fprintf(file, "\nSimplex stats: %s\n", message.c_str()); fprintf(file, " valid = %d\n", this->valid); + fprintf(file, " num_col = %d\n", + this->num_col); + fprintf(file, " num_row = %d\n", + this->num_row); + fprintf(file, " num_nz = %d\n", + this->num_nz); fprintf(file, " iteration_count = %d\n", this->iteration_count); fprintf(file, " num_invert = %d\n", this->num_invert); @@ -4461,6 +4471,9 @@ void HighsSimplexStats::report(FILE* file, std::string message, const HighsInt s void HighsSimplexStats::initialise(const HighsInt iteration_count_) { valid = false; iteration_count = -iteration_count_; + num_col = 0; + num_row = 0; + num_nz = 0; num_invert = 0; last_invert_num_el = 0; last_factored_basis_num_el = 0; @@ -4469,3 +4482,22 @@ void HighsSimplexStats::initialise(const HighsInt iteration_count_) { row_ap_density = 0; row_DSE_density = 0; } + +double HighsSimplexStats::workEstimate() { + double work = 0; + // INVERT + work += num_invert*(2*last_factored_basis_num_el + 2*num_row); + // Compute primal + work += num_invert*(last_factored_basis_num_el + num_nz-last_invert_num_el); + // Compute dual + work += num_invert*(last_factored_basis_num_el + num_nz-last_invert_num_el); + // BTRAN + work += iteration_count*num_row*row_ep_density; + // PRICE + work += iteration_count*row_ep_density*(num_nz-last_invert_num_el) + num_col*row_ap_density; + // FTRAN + work += iteration_count*num_row*col_aq_density; + // FTRAN_DSE + work += iteration_count*num_row*row_DSE_density; + return work; +} From 877d52e3bc20d2884e7582f64c04156acaccf4b1 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Sun, 1 Dec 2024 15:11:47 +0000 Subject: [PATCH 25/40] Gathered IPX stats from ComputeStartingPoint! --- src/ipm/IpxWrapper.cpp | 42 ++++++++++++++++++++++++++++++++++ src/ipm/ipx/control.h | 1 + src/ipm/ipx/ipm.cc | 34 +++++++++++++++------------ src/ipm/ipx/ipm.h | 3 ++- src/ipm/ipx/ipx_parameters.h | 3 ++- src/ipm/ipx/kkt_solver.h | 6 ++++- src/ipm/ipx/kkt_solver_basis.h | 1 + src/ipm/ipx/kkt_solver_diag.h | 3 ++- src/ipm/ipx/lp_solver.cc | 7 +++++- src/ipm/ipx/lp_solver.h | 2 ++ src/lp_data/HighsSolverStats.h | 14 ++++++------ src/simplex/HEkk.cpp | 21 +++++++++-------- 12 files changed, 101 insertions(+), 36 deletions(-) diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 7a073eeba5..5fc6ad4714 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -1086,3 +1086,45 @@ void reportSolveData(const HighsLogOptions& log_options, " Volume increase = %11.4g\n\n", ipx_info.volume_increase); } + +double HighsIpxStats::workEstimate() const { + return 0; +} + +void HighsIpxStats::report(FILE* file, const std::string message, const HighsInt style) const { + if (style == HighsSolverStatsReportPretty) { + fprintf(file, "\nIpx stats: %s\n", message.c_str()); + fprintf(file, " valid = %d\n", this->valid); + fprintf(file, " num_col = %d\n", + this->num_col); + fprintf(file, " num_row = %d\n", + this->num_row); + fprintf(file, " num_nz = %d\n", + this->num_nz); + fprintf(file, " iteration_count = %d\n", + this->iteration_count); + assert(iteration_count == HighsInt(cr_count.size())); + assert(iteration_count == HighsInt(invert_num_el.size())); + assert(iteration_count == HighsInt(factored_basis_num_el.size())); + fprintf(file, " Iter cr_count basisNz invertNz\n"); + for (HighsInt iteration = 0; iteration < iteration_count; iteration++) { + fprintf(file, " %4d %5d %7d %7d\n", + int(iteration), + int(cr_count[iteration]), + int(factored_basis_num_el[iteration]), + int(invert_num_el[iteration])); + } + } +} + +void HighsIpxStats::initialise() { + valid = false; + iteration_count = 0; + num_col = 0; + num_row = 0; + num_nz = 0; + iteration_count = 0; + cr_count.clear(); + factored_basis_num_el.clear(); + invert_num_el.clear(); +} diff --git a/src/ipm/ipx/control.h b/src/ipm/ipx/control.h index 482677cf19..d70933eae9 100644 --- a/src/ipm/ipx/control.h +++ b/src/ipm/ipx/control.h @@ -95,6 +95,7 @@ class Control { double centringAlphaScaling() const{return parameters_.centring_alpha_scaling; } ipxint badProductsTolerance() const{return parameters_.bad_products_tolerance; } HighsSimplexStats simplexStats() const{return parameters_.simplex_stats; } + HighsIpxStats ipxStats() const{return parameters_.ipx_stats; } const Parameters& parameters() const; void parameters(const Parameters& new_parameters); diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index a2ff8e9aaa..df55395ce4 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -20,10 +20,11 @@ struct IPM::Step { IPM::IPM(const Control& control) : control_(control) {} -void IPM::StartingPoint(KKTSolver* kkt, Iterate* iterate, Info* info) { + void IPM::StartingPoint(KKTSolver* kkt, Iterate* iterate, Info* info, HighsIpxStats* ipx_stats) { kkt_ = kkt; iterate_ = iterate; info_ = info; + ipx_stats_ = ipx_stats; PrintHeader(); ComputeStartingPoint(); if (info->errflag == 0) @@ -97,21 +98,15 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { break; MakeStep(step); // - std::stringstream h_logging_stream; - h_logging_stream << "IPM::Driver fill factor = " << - kkt_->current_fill() << "; iter (sum = " << - kkt_->iterSum() << ", max = " << - kkt_->iterMax() << ")\n"; - control_.hLog(h_logging_stream); - // Find nnz for L and U - // - // incopororate kkt_->current_fill() * - double ipm_cost_measure = kkt_->iterSum(); + // Get deeper IPX stats unavailable at this level + HighsIpxStats control_ipx_stats = control_.ipxStats(); + HighsSimplexStats simplex_stats = control_.simplexStats(); - simplex_stats.report(stdout, "In ipm.cpp"); - double simplex_cost_measure = simplex_stats.iteration_count; + // simplex_stats.report(stdout, "In ipm.cpp"); + double simplex_work_measure = simplex_stats.workEstimate(); // info->iter++; + PrintOutput(); } @@ -217,7 +212,9 @@ void IPM::ComputeStartingPoint() { Vector x(n+m), xl(n+m), xu(n+m), y(m), zl(n+m), zu(n+m); Vector rb(m); // workspace - // Factorize the KKT matrix with the identity matrix in the (1,1) block. + // "Factorize" the KKT matrix with the identity matrix in the (1,1) block. + // + // Just inverts the diagonal of the normal matrix kkt_->Factorize(nullptr, info_); if (info_->errflag) return; @@ -237,6 +234,7 @@ void IPM::ComputeStartingPoint() { } double tol = 0.1 * Infnorm(rb); zl = 0.0; + Int kktiter1_before = info_->kktiter1; kkt_->Solve(zl, rb, tol, xl, y, info_); if (info_->errflag) return; @@ -339,6 +337,14 @@ void IPM::ComputeStartingPoint() { } iterate_->Initialize(x, xl, xu, y, zl, zu); best_complementarity_ = iterate_->complementarity(); + // Gather deep stats and update local stats + Int kkt_iter1 = info_->kktiter1 - kktiter1_before; + + ipx_stats_->iteration_count++; + ipx_stats_->cr_count.push_back(kkt_iter1); + ipx_stats_->factored_basis_num_el.push_back(m); + ipx_stats_->invert_num_el.push_back(m); + ipx_stats_->report(stdout, "IPM::ComputeStartingPoint()"); } // Computes maximum alpha such that x + alpha*dx >= 0. diff --git a/src/ipm/ipx/ipm.h b/src/ipm/ipx/ipm.h index 7d5ae83e60..c5e937167b 100644 --- a/src/ipm/ipx/ipm.h +++ b/src/ipm/ipx/ipm.h @@ -25,7 +25,7 @@ class IPM { // IPX_STATUS_time_limit if the KKT solver was interrupted by time limit, // IPX_STATUS_failed if the KKT solver failed with info->errflag. // If the method did not terminate successfully, @iterate is unchanged. - void StartingPoint(KKTSolver* kkt, Iterate* iterate, Info* info); + void StartingPoint(KKTSolver* kkt, Iterate* iterate, Info* info, HighsIpxStats* ipx_stats); // Updates @iterate by interior point iterations. On return ipm_status is // IPX_STATUS_optimal if iterate->term_crit_reached() is true, @@ -74,6 +74,7 @@ class IPM { KKTSolver* kkt_{nullptr}; Iterate* iterate_{nullptr}; Info* info_{nullptr}; + HighsIpxStats* ipx_stats_{nullptr}; double step_primal_{0.0}, step_dual_{0.0}; // Counts the # bad iterations since the last good iteration. An iteration diff --git a/src/ipm/ipx/ipx_parameters.h b/src/ipm/ipx/ipx_parameters.h index c19ddea438..5ea11bc3aa 100644 --- a/src/ipm/ipx/ipx_parameters.h +++ b/src/ipm/ipx/ipx_parameters.h @@ -70,8 +70,9 @@ struct ipx_parameters { bool highs_logging; const HighsLogOptions* log_options; - /* Simplex solution stats */ + /* Simplex and IPX solution stats */ HighsSimplexStats simplex_stats; + HighsIpxStats ipx_stats; }; #ifdef __cplusplus diff --git a/src/ipm/ipx/kkt_solver.h b/src/ipm/ipx/kkt_solver.h index 7d3fe8f395..4c809c3121 100644 --- a/src/ipm/ipx/kkt_solver.h +++ b/src/ipm/ipx/kkt_solver.h @@ -56,7 +56,10 @@ class KKTSolver { // call to Factorize(). Otherwise returns 0. Int basis_changes() const; - // + // Number of nonzeros in the matrix to be factored + Int matrix_nz() const; + + // Fill-factor for the factored matrix double current_fill() const; // If a basis matrix is maintained, returns a pointer to it. @@ -70,6 +73,7 @@ class KKTSolver { virtual Int _iterSum() const = 0; virtual Int _iterMax() const = 0; virtual Int _basis_changes() const { return 0; } + virtual Int _matrix_nz() const { return 0; } virtual double _current_fill() const { return 0.0; } virtual const Basis* _basis() const { return nullptr; } }; diff --git a/src/ipm/ipx/kkt_solver_basis.h b/src/ipm/ipx/kkt_solver_basis.h index b155211926..be766144fe 100644 --- a/src/ipm/ipx/kkt_solver_basis.h +++ b/src/ipm/ipx/kkt_solver_basis.h @@ -37,6 +37,7 @@ class KKTSolverBasis : public KKTSolver { Int _iterMax() const override { return iter_max_; } Int _basis_changes() const override { return basis_changes_; } + Int _matrix_nz() const override { return _basis()->matrix_nz(); } double _current_fill() const override { return _basis()->current_fill(); } const Basis* _basis() const override { return &basis_; } diff --git a/src/ipm/ipx/kkt_solver_diag.h b/src/ipm/ipx/kkt_solver_diag.h index 6de0e94523..464e9954d0 100644 --- a/src/ipm/ipx/kkt_solver_diag.h +++ b/src/ipm/ipx/kkt_solver_diag.h @@ -24,7 +24,8 @@ class KKTSolverDiag : public KKTSolver { Int maxiter() const { return maxiter_; } void maxiter(Int new_maxiter) { maxiter_ = new_maxiter; } - // double current_fill() const { return 0; } + double current_fill() const { return 0; } + Int matrix_nz() const { return 0; } private: void _Factorize(Iterate* iterate, Info* info) override; diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index 398a8764d3..872cba468d 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -392,6 +392,11 @@ void LpSolver::RunIPM() { info_.centring_tried = false; info_.centring_success = false; + ipx_stats_.initialise(); + ipx_stats_.num_col = model_.cols(); + ipx_stats_.num_row = model_.rows(); + ipx_stats_.num_nz = model_.AI().entries(); + if (x_start_.size() != 0) { control_.hLog(" Using starting point provided by user. Skipping initial iterations.\n"); iterate_->Initialize(x_start_, xl_start_, xu_start_, @@ -474,7 +479,7 @@ void LpSolver::ComputeStartingPoint(IPM& ipm) { // If the starting point procedure fails, then iterate_ remains as // initialized by the constructor, which is a valid state for // postprocessing/postsolving. - ipm.StartingPoint(&kkt, iterate_.get(), &info_); + ipm.StartingPoint(&kkt, iterate_.get(), &info_, &ipx_stats_); info_.time_ipm1 += timer.Elapsed(); } diff --git a/src/ipm/ipx/lp_solver.h b/src/ipm/ipx/lp_solver.h index 18eb70606f..5076da0f4f 100644 --- a/src/ipm/ipx/lp_solver.h +++ b/src/ipm/ipx/lp_solver.h @@ -163,6 +163,8 @@ class LpSolver { // Returns -1 if no basis was available and 0 otherwise. Int SymbolicInvert(Int* rowcounts, Int* colcounts); + HighsIpxStats ipx_stats_; + private: void ClearSolution(); void InteriorPointSolve(); diff --git a/src/lp_data/HighsSolverStats.h b/src/lp_data/HighsSolverStats.h index ffa8323dd1..810d3e585c 100644 --- a/src/lp_data/HighsSolverStats.h +++ b/src/lp_data/HighsSolverStats.h @@ -31,13 +31,13 @@ struct HighsSimplexStats { HighsInt num_nz; HighsInt iteration_count; HighsInt num_invert; - HighsInt last_invert_num_el; HighsInt last_factored_basis_num_el; + HighsInt last_invert_num_el; double col_aq_density; double row_ep_density; double row_ap_density; double row_DSE_density; - double workEstimate(); + double workEstimate() const; void report(FILE* file, const std::string message = "", const HighsInt style = HighsSolverStatsReportPretty) const; @@ -49,11 +49,11 @@ struct HighsIpxStats { HighsInt num_col; HighsInt num_row; HighsInt num_nz; - HighsInt ipm_iteration_count; - HighsInt average_cr_count; - HighsInt last_invert_num_el; - HighsInt last_factored_basis_num_el; - double workEstimate(); + HighsInt iteration_count; + std::vector cr_count; + std::vector factored_basis_num_el; + std::vector invert_num_el; + double workEstimate() const; void report(FILE* file, const std::string message = "", const HighsInt style = HighsSolverStatsReportPretty) const; void initialise(); diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index a3dddcf51f..01db73686a 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -4442,10 +4442,10 @@ void HighsSimplexStats::report(FILE* file, std::string message, const HighsInt s 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, " 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); @@ -4453,14 +4453,15 @@ void HighsSimplexStats::report(FILE* file, std::string message, const HighsInt s this->row_DSE_density); } else if (style == HighsSolverStatsReportCsvHeader) { fprintf(file, - "valid,iteration_count,num_invert,last_invert_num_el,last_factored_" - "basis_num_el,col_aq_density,row_ep_density,row_ap_density,row_DSE_" + "valid,iteration_count,num_invert,last_factored_basis_num_el,last_invert_num_el," + "col_aq_density,row_ep_density,row_ap_density,row_DSE_" "density,"); } else if (style == HighsSolverStatsReportCsvData) { fprintf(file, "%d,%d,%d,%d,%d,%g,%g,%g,%g,", int(this->valid), int(this->iteration_count), int(this->num_invert), + int(this->last_factored_basis_num_el), int(this->last_invert_num_el), - int(this->last_factored_basis_num_el), this->col_aq_density, + this->col_aq_density, this->row_ep_density, this->row_ap_density, this->row_DSE_density); } else { fprintf(file, "Unknown simplex stats report style of %d\n", int(style)); @@ -4475,26 +4476,26 @@ void HighsSimplexStats::initialise(const HighsInt iteration_count_) { num_row = 0; num_nz = 0; num_invert = 0; - last_invert_num_el = 0; last_factored_basis_num_el = 0; + last_invert_num_el = 0; col_aq_density = 0; row_ep_density = 0; row_ap_density = 0; row_DSE_density = 0; } -double HighsSimplexStats::workEstimate() { +double HighsSimplexStats::workEstimate() const { double work = 0; // INVERT work += num_invert*(2*last_factored_basis_num_el + 2*num_row); // Compute primal - work += num_invert*(last_factored_basis_num_el + num_nz-last_invert_num_el); + work += num_invert*(last_invert_num_el + num_nz-last_factored_basis_num_el); // Compute dual - work += num_invert*(last_factored_basis_num_el + num_nz-last_invert_num_el); + work += num_invert*(last_invert_num_el + num_nz-last_factored_basis_num_el); // BTRAN work += iteration_count*num_row*row_ep_density; // PRICE - work += iteration_count*row_ep_density*(num_nz-last_invert_num_el) + num_col*row_ap_density; + work += iteration_count*row_ep_density*(num_nz-last_factored_basis_num_el) + num_col*row_ap_density; // FTRAN work += iteration_count*num_row*col_aq_density; // FTRAN_DSE From 70ac00f3d9882836e78336cc9ab084db2036b82f Mon Sep 17 00:00:00 2001 From: JAJHall Date: Sun, 1 Dec 2024 17:42:56 +0000 Subject: [PATCH 26/40] Now to return INVERT nz from basiclu --- src/ipm/IpxWrapper.cpp | 36 +++++++++++++++++++++++++-------- src/ipm/ipx/ipm.cc | 29 ++++++++++++++++++++------ src/ipm/ipx/kkt_solver.cc | 3 ++- src/ipm/ipx/kkt_solver.h | 6 +++--- src/ipm/ipx/kkt_solver_basis.cc | 2 ++ src/ipm/ipx/kkt_solver_basis.h | 6 ++++-- src/ipm/ipx/kkt_solver_diag.h | 4 ++-- src/lp_data/HighsSolverStats.h | 1 + 8 files changed, 65 insertions(+), 22 deletions(-) diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 5fc6ad4714..5b4e6028d9 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -1103,16 +1103,35 @@ void HighsIpxStats::report(FILE* file, const std::string message, const HighsInt this->num_nz); fprintf(file, " iteration_count = %d\n", this->iteration_count); - assert(iteration_count == HighsInt(cr_count.size())); - assert(iteration_count == HighsInt(invert_num_el.size())); - assert(iteration_count == HighsInt(factored_basis_num_el.size())); - fprintf(file, " Iter cr_count basisNz invertNz\n"); + if (this->iteration_count != HighsInt(cr_type.size())) + printf("iteration_count = %d != %d = cr_type.size()\n", + int(this->iteration_count), + int(this->cr_type.size())); + if (this->iteration_count != HighsInt(cr_count.size())) + printf("iteration_count = %d != %d = cr_count.size()\n", + int(this->iteration_count), + int(this->cr_count.size())); + if (this->iteration_count != HighsInt(invert_num_el.size())) + printf("iteration_count = %d != %d = invert_num_el.size()\n", + int(this->iteration_count), + int(this->invert_num_el.size())); + if (this->iteration_count != HighsInt(factored_basis_num_el.size())) + printf("iteration_count = %d != %d = factored_basis_num_el.size()\n", + int(this->iteration_count), + int(this->factored_basis_num_el.size())); + assert(this->iteration_count == HighsInt(this->cr_type.size())); + assert(this->iteration_count == HighsInt(this->cr_count.size())); + assert(this->iteration_count == HighsInt(this->invert_num_el.size())); + assert(this->iteration_count == HighsInt(this->factored_basis_num_el.size())); + fprintf(file, " Iter type cr_count basisNz invertNz\n"); + //rintf(file, " dddd d ddddd ddddddd ddddddd\n"); for (HighsInt iteration = 0; iteration < iteration_count; iteration++) { - fprintf(file, " %4d %5d %7d %7d\n", + fprintf(file, " %4d %1d %5d %7d %7d\n", int(iteration), - int(cr_count[iteration]), - int(factored_basis_num_el[iteration]), - int(invert_num_el[iteration])); + int(this->cr_type[iteration]), + int(this->cr_count[iteration]), + int(this->factored_basis_num_el[iteration]), + int(this->invert_num_el[iteration])); } } } @@ -1124,6 +1143,7 @@ void HighsIpxStats::initialise() { num_row = 0; num_nz = 0; iteration_count = 0; + cr_type.clear(); cr_count.clear(); factored_basis_num_el.clear(); invert_num_el.clear(); diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index df55395ce4..2dce396dad 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -87,6 +87,8 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { } if ((info->errflag = control_.InterruptCheck(info->iter)) != 0) break; + Int kktiter1 = -info_->kktiter1; + Int kktiter2 = -info_->kktiter2; kkt->Factorize(iterate, info); if (info->errflag) break; @@ -107,6 +109,20 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { // info->iter++; + // Update IPX stats + kktiter1 += info_->kktiter1; + kktiter2 += info_->kktiter2; + Int cr_type = kktiter1 > 0 ? 1 : 2; + Int kktiter = cr_type == 1 ? kktiter1 : kktiter2; + if (cr_type == 1) assert(kktiter2 == 0); + + ipx_stats_->iteration_count++; + ipx_stats_->cr_type.push_back(cr_type); + ipx_stats_->cr_count.push_back(kktiter); + ipx_stats_->factored_basis_num_el.push_back(kkt_->matrix_nz()); + ipx_stats_->invert_num_el.push_back(kkt_->invert_nz()); + if (cr_type == 2) ipx_stats_->report(stdout, "IPM::Driver()"); + PrintOutput(); } @@ -234,7 +250,7 @@ void IPM::ComputeStartingPoint() { } double tol = 0.1 * Infnorm(rb); zl = 0.0; - Int kktiter1_before = info_->kktiter1; + Int kktiter1 = -info_->kktiter1; kkt_->Solve(zl, rb, tol, xl, y, info_); if (info_->errflag) return; @@ -337,13 +353,14 @@ void IPM::ComputeStartingPoint() { } iterate_->Initialize(x, xl, xu, y, zl, zu); best_complementarity_ = iterate_->complementarity(); - // Gather deep stats and update local stats - Int kkt_iter1 = info_->kktiter1 - kktiter1_before; + // Update IPX stats + kktiter1 += info_->kktiter1; ipx_stats_->iteration_count++; - ipx_stats_->cr_count.push_back(kkt_iter1); - ipx_stats_->factored_basis_num_el.push_back(m); - ipx_stats_->invert_num_el.push_back(m); + ipx_stats_->cr_type.push_back(1); + ipx_stats_->cr_count.push_back(kktiter1); + ipx_stats_->factored_basis_num_el.push_back(kkt_->matrix_nz()); + ipx_stats_->invert_num_el.push_back(kkt_->invert_nz()); ipx_stats_->report(stdout, "IPM::ComputeStartingPoint()"); } diff --git a/src/ipm/ipx/kkt_solver.cc b/src/ipm/ipx/kkt_solver.cc index c0d4da2397..7f8da5fc2f 100644 --- a/src/ipm/ipx/kkt_solver.cc +++ b/src/ipm/ipx/kkt_solver.cc @@ -19,7 +19,8 @@ void KKTSolver::Solve(const Vector& a, const Vector& b, double tol, Int KKTSolver::iterSum() const { return _iterSum(); } Int KKTSolver::iterMax() const { return _iterMax(); } Int KKTSolver::basis_changes() const { return _basis_changes(); } -double KKTSolver::current_fill() const {return _current_fill(); } +Int KKTSolver::matrix_nz() const {return _matrix_nz(); } +Int KKTSolver::invert_nz() const {return _invert_nz(); } const Basis* KKTSolver::basis() const { return _basis(); } } // namespace ipx diff --git a/src/ipm/ipx/kkt_solver.h b/src/ipm/ipx/kkt_solver.h index 4c809c3121..78bcd8b32a 100644 --- a/src/ipm/ipx/kkt_solver.h +++ b/src/ipm/ipx/kkt_solver.h @@ -59,8 +59,8 @@ class KKTSolver { // Number of nonzeros in the matrix to be factored Int matrix_nz() const; - // Fill-factor for the factored matrix - double current_fill() const; + // Number of nonzeros in the factored matrix + Int invert_nz() const; // If a basis matrix is maintained, returns a pointer to it. // Otherwise returns NULL. @@ -74,7 +74,7 @@ class KKTSolver { virtual Int _iterMax() const = 0; virtual Int _basis_changes() const { return 0; } virtual Int _matrix_nz() const { return 0; } - virtual double _current_fill() const { return 0.0; } + virtual Int _invert_nz() const { return 0; } virtual const Basis* _basis() const { return nullptr; } }; diff --git a/src/ipm/ipx/kkt_solver_basis.cc b/src/ipm/ipx/kkt_solver_basis.cc index 46d28e2766..1f2125e62c 100644 --- a/src/ipm/ipx/kkt_solver_basis.cc +++ b/src/ipm/ipx/kkt_solver_basis.cc @@ -59,6 +59,8 @@ void KKTSolverBasis::_Factorize(Iterate* iterate, Info* info) { if (info->errflag) return; } + matrix_nz_ = basis_.matrix_nz(); + invert_nz_ = 0; splitted_normal_matrix_.Prepare(basis_, &colscale_[0]); factorized_ = true; diff --git a/src/ipm/ipx/kkt_solver_basis.h b/src/ipm/ipx/kkt_solver_basis.h index be766144fe..d7a071e0a0 100644 --- a/src/ipm/ipx/kkt_solver_basis.h +++ b/src/ipm/ipx/kkt_solver_basis.h @@ -37,8 +37,8 @@ class KKTSolverBasis : public KKTSolver { Int _iterMax() const override { return iter_max_; } Int _basis_changes() const override { return basis_changes_; } - Int _matrix_nz() const override { return _basis()->matrix_nz(); } - double _current_fill() const override { return _basis()->current_fill(); } + Int _matrix_nz() const override { return matrix_nz_; } + Int _invert_nz() const override { return invert_nz_; } const Basis* _basis() const override { return &basis_; } // Processes basic variables that are close to a bound by either pivoting @@ -64,6 +64,8 @@ class KKTSolverBasis : public KKTSolver { Int iter_sum_{0}; Int iter_max_{0}; Int basis_changes_{0}; + Int matrix_nz_{0}; + Int invert_nz_{0}; }; } // namespace ipx diff --git a/src/ipm/ipx/kkt_solver_diag.h b/src/ipm/ipx/kkt_solver_diag.h index 464e9954d0..05d6f1185c 100644 --- a/src/ipm/ipx/kkt_solver_diag.h +++ b/src/ipm/ipx/kkt_solver_diag.h @@ -24,8 +24,8 @@ class KKTSolverDiag : public KKTSolver { Int maxiter() const { return maxiter_; } void maxiter(Int new_maxiter) { maxiter_ = new_maxiter; } - double current_fill() const { return 0; } - Int matrix_nz() const { return 0; } + Int matrix_nz() const { return 0; } + Int invert_nz() const { return 0; } private: void _Factorize(Iterate* iterate, Info* info) override; diff --git a/src/lp_data/HighsSolverStats.h b/src/lp_data/HighsSolverStats.h index 810d3e585c..dd19b8f2fa 100644 --- a/src/lp_data/HighsSolverStats.h +++ b/src/lp_data/HighsSolverStats.h @@ -50,6 +50,7 @@ struct HighsIpxStats { HighsInt num_row; HighsInt num_nz; HighsInt iteration_count; + std::vector cr_type; std::vector cr_count; std::vector factored_basis_num_el; std::vector invert_num_el; From d45afb220aed65bd319cab4250618304bc962709 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Sun, 1 Dec 2024 19:18:19 +0000 Subject: [PATCH 27/40] Now extracting matrix_nz and invert_nz --- src/ipm/ipx/basiclu_wrapper.cc | 8 +++++++- src/ipm/ipx/basiclu_wrapper.h | 3 +++ src/ipm/ipx/basis.cc | 7 +++++++ src/ipm/ipx/basis.h | 2 ++ src/ipm/ipx/forrest_tomlin.cc | 2 ++ src/ipm/ipx/forrest_tomlin.h | 1 + src/ipm/ipx/ipm.cc | 9 +++++++-- src/ipm/ipx/lu_update.cc | 4 +++- src/ipm/ipx/lu_update.h | 4 ++++ 9 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/ipm/ipx/basiclu_wrapper.cc b/src/ipm/ipx/basiclu_wrapper.cc index b22697ec34..91fede7333 100644 --- a/src/ipm/ipx/basiclu_wrapper.cc +++ b/src/ipm/ipx/basiclu_wrapper.cc @@ -58,6 +58,8 @@ Int BasicLu::_Factorize(const Int* Bbegin, const Int* Bend, const Int* Bi, Int lnz = xstore_[BASICLU_LNZ]; Int unz = xstore_[BASICLU_UNZ]; Int dim = xstore_[BASICLU_DIM]; + matrix_nz_ = matrix_nz; + invert_nz_ = lnz + unz + dim; fill_factor_ = 1.0 * (lnz+unz+dim) / matrix_nz; double normLinv = xstore_[BASICLU_NORMEST_LINV]; @@ -254,7 +256,11 @@ bool BasicLu::_NeedFreshFactorization() { } Int BasicLu::_matrix_nz() const { - return xstore_[BASICLU_MATRIX_NZ]; + return matrix_nz_;//xstore_[BASICLU_MATRIX_NZ]; +} + +Int BasicLu::_invert_nz() const { + return invert_nz_;//xstore_[BASICLU_LNZ] + xstore_[BASICLU_UNZ] + xstore_[BASICLU_DIM]; } double BasicLu::_fill_factor() const { diff --git a/src/ipm/ipx/basiclu_wrapper.h b/src/ipm/ipx/basiclu_wrapper.h index 669fa7b9d1..b2e04a903b 100644 --- a/src/ipm/ipx/basiclu_wrapper.h +++ b/src/ipm/ipx/basiclu_wrapper.h @@ -25,6 +25,7 @@ class BasicLu : public LuUpdate { Int _Update(double pivot) override; bool _NeedFreshFactorization() override; Int _matrix_nz() const override; + Int _invert_nz() const override; double _fill_factor() const override; double _pivottol() const override; void _pivottol(double new_pivottol) override; @@ -40,6 +41,8 @@ class BasicLu : public LuUpdate { std::vector xstore_; std::vector Li_, Ui_, Wi_; std::vector Lx_, Ux_, Wx_; + Int matrix_nz_; + Int invert_nz_; double fill_factor_; }; diff --git a/src/ipm/ipx/basis.cc b/src/ipm/ipx/basis.cc index 823873b50c..af67a0428a 100644 --- a/src/ipm/ipx/basis.cc +++ b/src/ipm/ipx/basis.cc @@ -131,6 +131,9 @@ Int Basis::Factorize() { AI.values(), false); num_factorizations_++; matrix_nz_ = lu_->matrix_nz(); + invert_nz_ = lu_->invert_nz(); + double fill_factor = lu_->fill_factor(); + printf("Basis::Factorize() nz (%d, %d) fill = %g\n", int(matrix_nz_), int(invert_nz_), fill_factor); fill_factors_.push_back(lu_->fill_factor()); if (flag & 2) { AdaptToSingularFactorization(); @@ -454,6 +457,10 @@ Int Basis::matrix_nz() const { return matrix_nz_; } +Int Basis::invert_nz() const { + return invert_nz_; +} + double Basis::current_fill() const { if (fill_factors_.empty()) return 0.0; diff --git a/src/ipm/ipx/basis.h b/src/ipm/ipx/basis.h index e8f7e2df57..82cdd27749 100644 --- a/src/ipm/ipx/basis.h +++ b/src/ipm/ipx/basis.h @@ -220,6 +220,7 @@ class Basis { double time_btran() const; // time BTRAN, including partial double time_update() const; // time LU update Int matrix_nz() const; // Current nonzeros of matrix to be factored + Int invert_nz() const; // Nonzeros of factored matrix double current_fill() const; // Current LU fill factors double mean_fill() const; // geom. mean of LU fill factors double max_fill() const; // max LU fill factor @@ -310,6 +311,7 @@ class Basis { double time_update_{0.0}; // time for LU updates double time_factorize_{0.0}; // time for LU factorizations Int matrix_nz_{0}; // nonzeros of matrix to be factored + Int invert_nz_{0}; // nonzeros of INVERT of factored matrix std::vector fill_factors_; // fill factors from LU factorizations double sum_ftran_density_{0.0}; double sum_btran_density_{0.0}; diff --git a/src/ipm/ipx/forrest_tomlin.cc b/src/ipm/ipx/forrest_tomlin.cc index d519db2de9..4d7f38c7a1 100644 --- a/src/ipm/ipx/forrest_tomlin.cc +++ b/src/ipm/ipx/forrest_tomlin.cc @@ -237,6 +237,8 @@ bool ForrestTomlin::_NeedFreshFactorization() { Int ForrestTomlin::_matrix_nz() const { return 0;} +Int ForrestTomlin::_invert_nz() const { return 0;} + double ForrestTomlin::_fill_factor() const { return fill_factor_; } diff --git a/src/ipm/ipx/forrest_tomlin.h b/src/ipm/ipx/forrest_tomlin.h index 4989b2d127..73a3134daf 100644 --- a/src/ipm/ipx/forrest_tomlin.h +++ b/src/ipm/ipx/forrest_tomlin.h @@ -45,6 +45,7 @@ class ForrestTomlin : public LuUpdate { Int _Update(double pivot) override; bool _NeedFreshFactorization() override; Int _matrix_nz() const override; + Int _invert_nz() const override; double _fill_factor() const override; double _pivottol() const override; void _pivottol(double new_pivottol) override; diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index 2dce396dad..d4b659a5d9 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -110,6 +110,9 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { info->iter++; // Update IPX stats + + + kktiter1 += info_->kktiter1; kktiter2 += info_->kktiter2; Int cr_type = kktiter1 > 0 ? 1 : 2; @@ -119,8 +122,10 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { ipx_stats_->iteration_count++; ipx_stats_->cr_type.push_back(cr_type); ipx_stats_->cr_count.push_back(kktiter); - ipx_stats_->factored_basis_num_el.push_back(kkt_->matrix_nz()); - ipx_stats_->invert_num_el.push_back(kkt_->invert_nz()); + Int matrix_nz = cr_type == 1 ? 0 : kkt_->basis()->matrix_nz(); + Int invert_nz = cr_type == 1 ? 0 : kkt_->basis()->invert_nz(); + ipx_stats_->factored_basis_num_el.push_back(matrix_nz); + ipx_stats_->invert_num_el.push_back(invert_nz); if (cr_type == 2) ipx_stats_->report(stdout, "IPM::Driver()"); PrintOutput(); diff --git a/src/ipm/ipx/lu_update.cc b/src/ipm/ipx/lu_update.cc index cb22df744e..a1734dc129 100644 --- a/src/ipm/ipx/lu_update.cc +++ b/src/ipm/ipx/lu_update.cc @@ -43,7 +43,9 @@ bool LuUpdate::NeedFreshFactorization() { return _NeedFreshFactorization(); } -Int LuUpdate::matrix_nz() const { return 0; } +Int LuUpdate::matrix_nz() const { return _matrix_nz(); } + +Int LuUpdate::invert_nz() const { return _invert_nz(); } double LuUpdate::fill_factor() const { return _fill_factor(); diff --git a/src/ipm/ipx/lu_update.h b/src/ipm/ipx/lu_update.h index de632c729b..87cd41b6a9 100644 --- a/src/ipm/ipx/lu_update.h +++ b/src/ipm/ipx/lu_update.h @@ -97,6 +97,9 @@ class LuUpdate { // Returns nnz(B) from the last factorization. Int matrix_nz() const; + // Returns (nnz(L)+nnz(U)) from the last factorization. + Int invert_nz() const; + // Returns (nnz(L)+nnz(U))/nnz(B) from the last factorization. double fill_factor() const; @@ -121,6 +124,7 @@ class LuUpdate { virtual Int _Update(double pivot) = 0; virtual bool _NeedFreshFactorization() = 0; virtual Int _matrix_nz() const = 0; + virtual Int _invert_nz() const = 0; virtual double _fill_factor() const = 0; virtual double _pivottol() const = 0; virtual void _pivottol(double new_pivottol) = 0; From 78b268d6b6ff3bfe86504df99d8cc958f6877f75 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Sun, 1 Dec 2024 20:00:07 +0000 Subject: [PATCH 28/40] Remvoved matrix_nz and invert_nz from kkt --- src/ipm/ipx/ipm.cc | 4 ++-- src/ipm/ipx/kkt_solver.cc | 2 -- src/ipm/ipx/kkt_solver.h | 8 -------- src/ipm/ipx/kkt_solver_basis.cc | 2 -- src/ipm/ipx/kkt_solver_basis.h | 4 ---- src/ipm/ipx/kkt_solver_diag.h | 2 -- 6 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index d4b659a5d9..6765551ad6 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -364,8 +364,8 @@ void IPM::ComputeStartingPoint() { ipx_stats_->iteration_count++; ipx_stats_->cr_type.push_back(1); ipx_stats_->cr_count.push_back(kktiter1); - ipx_stats_->factored_basis_num_el.push_back(kkt_->matrix_nz()); - ipx_stats_->invert_num_el.push_back(kkt_->invert_nz()); + ipx_stats_->factored_basis_num_el.push_back(0); + ipx_stats_->invert_num_el.push_back(0); ipx_stats_->report(stdout, "IPM::ComputeStartingPoint()"); } diff --git a/src/ipm/ipx/kkt_solver.cc b/src/ipm/ipx/kkt_solver.cc index 7f8da5fc2f..a828fcb0a3 100644 --- a/src/ipm/ipx/kkt_solver.cc +++ b/src/ipm/ipx/kkt_solver.cc @@ -19,8 +19,6 @@ void KKTSolver::Solve(const Vector& a, const Vector& b, double tol, Int KKTSolver::iterSum() const { return _iterSum(); } Int KKTSolver::iterMax() const { return _iterMax(); } Int KKTSolver::basis_changes() const { return _basis_changes(); } -Int KKTSolver::matrix_nz() const {return _matrix_nz(); } -Int KKTSolver::invert_nz() const {return _invert_nz(); } const Basis* KKTSolver::basis() const { return _basis(); } } // namespace ipx diff --git a/src/ipm/ipx/kkt_solver.h b/src/ipm/ipx/kkt_solver.h index 78bcd8b32a..e5ab15bc22 100644 --- a/src/ipm/ipx/kkt_solver.h +++ b/src/ipm/ipx/kkt_solver.h @@ -56,12 +56,6 @@ class KKTSolver { // call to Factorize(). Otherwise returns 0. Int basis_changes() const; - // Number of nonzeros in the matrix to be factored - Int matrix_nz() const; - - // Number of nonzeros in the factored matrix - Int invert_nz() const; - // If a basis matrix is maintained, returns a pointer to it. // Otherwise returns NULL. const Basis* basis() const; @@ -73,8 +67,6 @@ class KKTSolver { virtual Int _iterSum() const = 0; virtual Int _iterMax() const = 0; virtual Int _basis_changes() const { return 0; } - virtual Int _matrix_nz() const { return 0; } - virtual Int _invert_nz() const { return 0; } virtual const Basis* _basis() const { return nullptr; } }; diff --git a/src/ipm/ipx/kkt_solver_basis.cc b/src/ipm/ipx/kkt_solver_basis.cc index 1f2125e62c..46d28e2766 100644 --- a/src/ipm/ipx/kkt_solver_basis.cc +++ b/src/ipm/ipx/kkt_solver_basis.cc @@ -59,8 +59,6 @@ void KKTSolverBasis::_Factorize(Iterate* iterate, Info* info) { if (info->errflag) return; } - matrix_nz_ = basis_.matrix_nz(); - invert_nz_ = 0; splitted_normal_matrix_.Prepare(basis_, &colscale_[0]); factorized_ = true; diff --git a/src/ipm/ipx/kkt_solver_basis.h b/src/ipm/ipx/kkt_solver_basis.h index d7a071e0a0..e2cf452de6 100644 --- a/src/ipm/ipx/kkt_solver_basis.h +++ b/src/ipm/ipx/kkt_solver_basis.h @@ -37,8 +37,6 @@ class KKTSolverBasis : public KKTSolver { Int _iterMax() const override { return iter_max_; } Int _basis_changes() const override { return basis_changes_; } - Int _matrix_nz() const override { return matrix_nz_; } - Int _invert_nz() const override { return invert_nz_; } const Basis* _basis() const override { return &basis_; } // Processes basic variables that are close to a bound by either pivoting @@ -64,8 +62,6 @@ class KKTSolverBasis : public KKTSolver { Int iter_sum_{0}; Int iter_max_{0}; Int basis_changes_{0}; - Int matrix_nz_{0}; - Int invert_nz_{0}; }; } // namespace ipx diff --git a/src/ipm/ipx/kkt_solver_diag.h b/src/ipm/ipx/kkt_solver_diag.h index 05d6f1185c..0b9ae559ae 100644 --- a/src/ipm/ipx/kkt_solver_diag.h +++ b/src/ipm/ipx/kkt_solver_diag.h @@ -24,8 +24,6 @@ class KKTSolverDiag : public KKTSolver { Int maxiter() const { return maxiter_; } void maxiter(Int new_maxiter) { maxiter_ = new_maxiter; } - Int matrix_nz() const { return 0; } - Int invert_nz() const { return 0; } private: void _Factorize(Iterate* iterate, Info* info) override; From aa522b95d2d09e657427d29a56b229fdabd3ca65 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Sun, 1 Dec 2024 21:05:11 +0000 Subject: [PATCH 29/40] Cleaned out unnecessary code, added when guessing how to get matrix_nz and invert_nz --- src/ipm/ipx/basiclu_wrapper.cc | 6 ++---- src/ipm/ipx/basis.cc | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ipm/ipx/basiclu_wrapper.cc b/src/ipm/ipx/basiclu_wrapper.cc index 91fede7333..cc129336bd 100644 --- a/src/ipm/ipx/basiclu_wrapper.cc +++ b/src/ipm/ipx/basiclu_wrapper.cc @@ -58,8 +58,6 @@ Int BasicLu::_Factorize(const Int* Bbegin, const Int* Bend, const Int* Bi, Int lnz = xstore_[BASICLU_LNZ]; Int unz = xstore_[BASICLU_UNZ]; Int dim = xstore_[BASICLU_DIM]; - matrix_nz_ = matrix_nz; - invert_nz_ = lnz + unz + dim; fill_factor_ = 1.0 * (lnz+unz+dim) / matrix_nz; double normLinv = xstore_[BASICLU_NORMEST_LINV]; @@ -256,11 +254,11 @@ bool BasicLu::_NeedFreshFactorization() { } Int BasicLu::_matrix_nz() const { - return matrix_nz_;//xstore_[BASICLU_MATRIX_NZ]; + return xstore_[BASICLU_MATRIX_NZ]; } Int BasicLu::_invert_nz() const { - return invert_nz_;//xstore_[BASICLU_LNZ] + xstore_[BASICLU_UNZ] + xstore_[BASICLU_DIM]; + return xstore_[BASICLU_LNZ] + xstore_[BASICLU_UNZ] + xstore_[BASICLU_DIM]; } double BasicLu::_fill_factor() const { diff --git a/src/ipm/ipx/basis.cc b/src/ipm/ipx/basis.cc index af67a0428a..0275d9945b 100644 --- a/src/ipm/ipx/basis.cc +++ b/src/ipm/ipx/basis.cc @@ -133,7 +133,6 @@ Int Basis::Factorize() { matrix_nz_ = lu_->matrix_nz(); invert_nz_ = lu_->invert_nz(); double fill_factor = lu_->fill_factor(); - printf("Basis::Factorize() nz (%d, %d) fill = %g\n", int(matrix_nz_), int(invert_nz_), fill_factor); fill_factors_.push_back(lu_->fill_factor()); if (flag & 2) { AdaptToSingularFactorization(); @@ -650,6 +649,7 @@ void Basis::CrashFactorize(Int* num_dropped) { AI.values(), true); num_factorizations_++; matrix_nz_ = lu_->matrix_nz(); + invert_nz_ = lu_->invert_nz(); fill_factors_.push_back(lu_->fill_factor()); Int ndropped = 0; if (flag & 2) From 47c99e3b8d0deb5cd643cdf31d460f3b320c63e1 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 2 Dec 2024 11:06:59 +0000 Subject: [PATCH 30/40] Introduced mip_old_analytic_centre_method option, and restricted mip_compute_analytic_centre to choose/off/on --- check/TestLpSolvers.cpp | 34 ++++++++++++++++++++++++- src/Highs.h | 19 ++++++++++++-- src/ipm/IpxWrapper.cpp | 41 +++++++++++++++++++++++++++++-- src/ipm/IpxWrapper.h | 2 +- src/ipm/ipx/ipm.cc | 9 ++----- src/ipm/ipx/lp_solver.cc | 4 +++ src/ipm/ipx/lp_solver.h | 3 +++ src/lp_data/HConst.h | 10 ++++---- src/lp_data/Highs.cpp | 9 +++++-- src/lp_data/HighsInterface.cpp | 6 ++--- src/lp_data/HighsLpSolverObject.h | 4 ++- src/lp_data/HighsOptions.h | 12 ++++++--- src/mip/HighsMipSolverData.cpp | 5 +--- src/mip/HighsPrimalHeuristics.cpp | 2 +- src/simplex/HEkk.cpp | 5 ++-- 15 files changed, 131 insertions(+), 34 deletions(-) diff --git a/check/TestLpSolvers.cpp b/check/TestLpSolvers.cpp index c22bd26868..522f13e01f 100644 --- a/check/TestLpSolvers.cpp +++ b/check/TestLpSolvers.cpp @@ -2,7 +2,7 @@ #include "Highs.h" #include "catch.hpp" -const bool dev_run = false; +const bool dev_run = true; struct IterationCount { HighsInt simplex; @@ -710,3 +710,35 @@ TEST_CASE("simplex-stats", "[highs_lp_solver]") { REQUIRE(simplex_stats.row_DSE_density > 0); if (dev_run) h.reportSimplexStats(stdout); } + +TEST_CASE("ipx-stats", "[highs_lp_solver]") { + HighsStatus return_status; + + Highs h; + const HighsIpxStats& ipx_stats = h.getIpxStats(); + h.setOptionValue("output_flag", dev_run); + // std::string model = "dcp2"; + std::string model = "adlittle"; + std::string model_file = + std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; + REQUIRE(h.readModel(model_file) == HighsStatus::kOk); + h.setOptionValue("solver", kIpmString); + REQUIRE(h.run() == HighsStatus::kOk); + REQUIRE(ipx_stats.valid); + REQUIRE(ipx_stats.num_col > 0); + REQUIRE(ipx_stats.num_row > 0); + REQUIRE(ipx_stats.num_nz > 0); + REQUIRE(ipx_stats.iteration_count > 0); + for (HighsInt iteration = 0; iteration < ipx_stats.iteration_count; iteration++) { + REQUIRE(ipx_stats.cr_count[iteration] > 0); + if (ipx_stats.cr_type[iteration] == 2) { + REQUIRE(ipx_stats.factored_basis_num_el[iteration] > 0); + REQUIRE(ipx_stats.invert_num_el[iteration] > 0); + } else { + REQUIRE(ipx_stats.cr_type[iteration] == 1); + REQUIRE(ipx_stats.factored_basis_num_el[iteration] == 0); + REQUIRE(ipx_stats.invert_num_el[iteration] == 0); + } + } + if (dev_run) h.reportIpxStats(stdout); +} diff --git a/src/Highs.h b/src/Highs.h index 00620b98a5..3d0039bcda 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1236,6 +1236,17 @@ class Highs { this->simplex_stats_ = simplex_stats; } + const HighsIpxStats& getIpxStats() const { return ipx_stats_; } + + void reportIpxStats( + FILE* file, const HighsInt style = HighsSolverStatsReportPretty) const { + ipx_stats_.report(file, "Original LP", style); + } + + void passIpxStats(const HighsIpxStats ipx_stats) { + this->ipx_stats_ = ipx_stats; + } + /** * @brief Get the hot start basis data from the most recent simplex * solve. Advanced method: for HiGHS MIP solver @@ -1453,6 +1464,7 @@ class Highs { HEkk ekk_instance_; HighsSimplexStats simplex_stats_; HighsSimplexStats presolved_lp_simplex_stats_; + HighsIpxStats ipx_stats_; HighsPresolveLog presolve_log_; @@ -1515,8 +1527,8 @@ class Highs { // // Invalidates all solver data in Highs class members by calling // invalidateModelStatus(), invalidateSolution(), invalidateBasis(), - // invalidateInfo(), invalidateEkk(), invalidateIis() and - // invalidateSimplexStats(); + // invalidateInfo(), invalidateEkk(), invalidateIis(), + // invalidateSimplexStats() and invalidateIpxStats(); void invalidateUserSolverData(); // // Invalidates the model status, solution_ and info_ @@ -1546,6 +1558,9 @@ class Highs { // Invalidates simplex_stats_ and presolved_lp_simplex_stats_; void invalidateSimplexStats(); + // Invalidates ipx_stats_ + void invalidateIpxStats(); + HighsStatus returnFromWriteSolution(FILE* file, const HighsStatus return_status); HighsStatus returnFromRun(const HighsStatus return_status, diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 5b4e6028d9..6be7d9e0ef 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -26,14 +26,14 @@ HighsStatus solveLpIpx(HighsLpSolverObject& solver_object) { solver_object.lp_, solver_object.ekk_instance_, solver_object.basis_, solver_object.solution_, solver_object.model_status_, solver_object.highs_info_, - solver_object.callback_); + solver_object.ipx_stats_, solver_object.callback_); } HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, const HighsLp& lp, const HEkk& ekk_instance, HighsBasis& highs_basis, HighsSolution& highs_solution, HighsModelStatus& model_status, HighsInfo& highs_info, - HighsCallback& callback) { + HighsIpxStats& ipx_stats, HighsCallback& callback) { // Use IPX to try to solve the LP // // Can return HighsModelStatus (HighsStatus) values: @@ -184,6 +184,8 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, const bool report_solve_data = kHighsAnalysisLevelSolverSummaryData & options.highs_analysis_level; // Get solver and solution information. + ipx_stats = lps.GetIpxStats(); + ipx_stats.valid = true; // Struct ipx_info defined in ipx/ipx_info.h const ipx::Info ipx_info = lps.GetInfo(); if (report_solve_data) reportSolveData(options.log_options, ipx_info); @@ -1133,6 +1135,41 @@ void HighsIpxStats::report(FILE* file, const std::string message, const HighsInt int(this->factored_basis_num_el[iteration]), int(this->invert_num_el[iteration])); } + } else if (style == HighsSolverStatsReportCsvHeader) { + fprintf(file, "valid,col,row,nz,iteration_count,cr_count,iteration_count, cr_count,matrix_nz,invert_nz,"); + } else if (style == HighsSolverStatsReportCsvData) { + HighsInt num_type1_iteration = 0; + HighsInt num_type2_iteration = 0; + double average_type1_cr_count = 0; + double average_type2_cr_count = 0; + double average_type2_matrix_nz= 0; + double average_type2_invert_nz = 0; + for (HighsInt iteration = 0; iteration < this->iteration_count; iteration++) { + if (this->cr_type[iteration] == 1) { + num_type1_iteration++; + average_type1_cr_count += this->cr_count[iteration]; + } else { + num_type2_iteration++; + average_type2_cr_count += this->cr_count[iteration]; + average_type2_matrix_nz += this->factored_basis_num_el[iteration]; + average_type2_invert_nz += this->invert_num_el[iteration]; + } + } + if (num_type1_iteration) + average_type1_cr_count /= (1.0 *num_type1_iteration); + if (num_type2_iteration) { + average_type2_cr_count /= (1.0 *num_type2_iteration); + average_type2_matrix_nz /= (1.0 *num_type2_iteration); + average_type2_invert_nz /= (1.0 *num_type2_iteration); + } + fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,", int(this->valid), + int(this->num_col), int(this->num_row), int(this->num_nz), + int(num_type1_iteration), int(average_type1_cr_count), + int(num_type2_iteration), int(average_type2_cr_count), + int(average_type2_matrix_nz), int(average_type2_invert_nz)); + } else { + fprintf(file, "Unknown IPX stats report style of %d\n", int(style)); + assert(123 == 456); } } diff --git a/src/ipm/IpxWrapper.h b/src/ipm/IpxWrapper.h index 792cf05346..16ccb6df0d 100644 --- a/src/ipm/IpxWrapper.h +++ b/src/ipm/IpxWrapper.h @@ -28,7 +28,7 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, const HighsLp& lp, const HEkk& ekk_instance, HighsBasis& highs_basis, HighsSolution& highs_solution, HighsModelStatus& model_status, HighsInfo& highs_info, - HighsCallback& callback); + HighsIpxStats& ipx_stats, HighsCallback& callback); void fillInIpxData(const HighsLp& lp, ipx::Int& num_col, ipx::Int& num_row, std::vector& obj, std::vector& col_lb, diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index 6765551ad6..024a63ae93 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -99,12 +99,7 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { if (info->errflag) break; MakeStep(step); - // - // Get deeper IPX stats unavailable at this level - HighsIpxStats control_ipx_stats = control_.ipxStats(); - HighsSimplexStats simplex_stats = control_.simplexStats(); - // simplex_stats.report(stdout, "In ipm.cpp"); double simplex_work_measure = simplex_stats.workEstimate(); // info->iter++; @@ -126,7 +121,7 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { Int invert_nz = cr_type == 1 ? 0 : kkt_->basis()->invert_nz(); ipx_stats_->factored_basis_num_el.push_back(matrix_nz); ipx_stats_->invert_num_el.push_back(invert_nz); - if (cr_type == 2) ipx_stats_->report(stdout, "IPM::Driver()"); + // if (cr_type == 2) ipx_stats_->report(stdout, "IPM::Driver()"); PrintOutput(); } @@ -366,7 +361,7 @@ void IPM::ComputeStartingPoint() { ipx_stats_->cr_count.push_back(kktiter1); ipx_stats_->factored_basis_num_el.push_back(0); ipx_stats_->invert_num_el.push_back(0); - ipx_stats_->report(stdout, "IPM::ComputeStartingPoint()"); + // ipx_stats_->report(stdout, "IPM::ComputeStartingPoint()"); } // Computes maximum alpha such that x + alpha*dx >= 0. diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index 872cba468d..b2eda8b370 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -128,6 +128,10 @@ Info LpSolver::GetInfo() const { return info_; } +HighsIpxStats LpSolver::GetIpxStats() const { + return ipx_stats_; +} + Int LpSolver::GetInteriorSolution(double* x, double* xl, double* xu, double* slack, double* y, double* zl, double* zu) const { diff --git a/src/ipm/ipx/lp_solver.h b/src/ipm/ipx/lp_solver.h index 5076da0f4f..1c97ffb27e 100644 --- a/src/ipm/ipx/lp_solver.h +++ b/src/ipm/ipx/lp_solver.h @@ -75,6 +75,9 @@ class LpSolver { // documentation for the meaning of Info values. Info GetInfo() const; + // Returns the IPX stats + HighsIpxStats GetIpxStats() const; + // Returns the final IPM iterate from the last call to Solve() into user // arrays. An iterate is available if GetInfo().status_ipm != // IPX_STATUS_not_run. If no iterate is available, the method does nothing. diff --git a/src/lp_data/HConst.h b/src/lp_data/HConst.h index d830f9a4fc..fcea91b853 100644 --- a/src/lp_data/HConst.h +++ b/src/lp_data/HConst.h @@ -46,11 +46,11 @@ const bool kAllowDeveloperAssert = false; const bool kExtendInvertWhenAddingRows = false; enum MipAnalyticCentreCalulation { - kMipAnalyticCentreCalulationMin = 0, - kMipAnalyticCentreCalulationNo = kMipAnalyticCentreCalulationMin, - kMipAnalyticCentreCalulationOriginal, - kMipAnalyticCentreCalulationTrue, - kMipAnalyticCentreCalulationMax = kMipAnalyticCentreCalulationTrue + kMipAnalyticCentreCalulationMin = -1, + kMipAnalyticCentreCalulationChoose = kMipAnalyticCentreCalulationMin, + kMipAnalyticCentreCalulationOff, + kMipAnalyticCentreCalulationOn, + kMipAnalyticCentreCalulationMax = kMipAnalyticCentreCalulationOn }; enum class HighsLogType { kInfo = 1, kDetailed, kVerbose, kWarning, kError }; diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index 004366372f..161587d944 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -2334,7 +2334,7 @@ HighsStatus Highs::setBasis(const HighsBasis& basis, HighsBasis modifiable_basis = basis; modifiable_basis.was_alien = true; HighsLpSolverObject solver_object(model_.lp_, modifiable_basis, solution_, - info_, ekk_instance_, callback_, + info_, ekk_instance_, ipx_stats_, callback_, options_, timer_); HighsStatus return_status = formSimplexLpBasisAndFactor(solver_object); if (return_status != HighsStatus::kOk) return HighsStatus::kError; @@ -3592,6 +3592,7 @@ void Highs::invalidateUserSolverData() { invalidateEkk(); invalidateIis(); invalidateSimplexStats(); + invalidateIpxStats(); } void Highs::invalidateModelStatusSolutionAndInfo() { @@ -3636,6 +3637,10 @@ void Highs::invalidateSimplexStats() { presolved_lp_simplex_stats_.initialise(); } +void Highs::invalidateIpxStats() { + ipx_stats_.initialise(); +} + HighsStatus Highs::completeSolutionFromDiscreteAssignment() { // Determine whether the current solution of a MIP is feasible and, // if not, try to assign values to continuous variables and discrete @@ -3777,7 +3782,7 @@ HighsStatus Highs::completeSolutionFromDiscreteAssignment() { HighsStatus Highs::callSolveLp(HighsLp& lp, const string message) { HighsStatus return_status = HighsStatus::kOk; - HighsLpSolverObject solver_object(lp, basis_, solution_, info_, ekk_instance_, + HighsLpSolverObject solver_object(lp, basis_, solution_, info_, ekk_instance_, ipx_stats_, callback_, options_, timer_); // Check that the model is column-wise diff --git a/src/lp_data/HighsInterface.cpp b/src/lp_data/HighsInterface.cpp index 1c7ad16b9f..23359c862a 100644 --- a/src/lp_data/HighsInterface.cpp +++ b/src/lp_data/HighsInterface.cpp @@ -1435,8 +1435,8 @@ HighsStatus Highs::getBasicVariablesInterface(HighsInt* basic_variables) { // The LP has no invert to use, so have to set one up, but only // for the current basis, so return_value is the rank deficiency. HighsLpSolverObject solver_object(lp, basis_, solution_, info_, - ekk_instance_, callback_, options_, - timer_); + ekk_instance_, ipx_stats_, + callback_, options_, timer_); const bool only_from_known_basis = true; return_status = interpretCallStatus( options_.log_options, @@ -1945,7 +1945,7 @@ HighsStatus Highs::getPrimalRayInterface(bool& has_primal_ray, HighsStatus Highs::getRangingInterface() { HighsLpSolverObject solver_object(model_.lp_, basis_, solution_, info_, - ekk_instance_, callback_, options_, timer_); + ekk_instance_, ipx_stats_, callback_, options_, timer_); solver_object.model_status_ = model_status_; return getRangingData(this->ranging_, solver_object); } diff --git a/src/lp_data/HighsLpSolverObject.h b/src/lp_data/HighsLpSolverObject.h index 895922487a..1d493f4012 100644 --- a/src/lp_data/HighsLpSolverObject.h +++ b/src/lp_data/HighsLpSolverObject.h @@ -21,7 +21,7 @@ class HighsLpSolverObject { public: HighsLpSolverObject(HighsLp& lp, HighsBasis& basis, HighsSolution& solution, - HighsInfo& highs_info, HEkk& ekk_instance, + HighsInfo& highs_info, HEkk& ekk_instance, HighsIpxStats& ipx_stats, HighsCallback& callback, HighsOptions& options, HighsTimer& timer) : lp_(lp), @@ -29,6 +29,7 @@ class HighsLpSolverObject { solution_(solution), highs_info_(highs_info), ekk_instance_(ekk_instance), + ipx_stats_(ipx_stats), callback_(callback), options_(options), timer_(timer) {} @@ -38,6 +39,7 @@ class HighsLpSolverObject { HighsSolution& solution_; HighsInfo& highs_info_; HEkk& ekk_instance_; + HighsIpxStats& ipx_stats_; HighsCallback& callback_; HighsOptions& options_; HighsTimer& timer_; diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index 6ae321368d..0da3e040a5 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -414,6 +414,7 @@ struct HighsOptionsStruct { // Options for MIP solver bool mip_detect_symmetry; bool mip_allow_restart; + bool mip_old_analytic_centre_method; HighsInt mip_max_nodes; HighsInt mip_max_stall_nodes; HighsInt mip_max_start_nodes; @@ -552,6 +553,7 @@ struct HighsOptionsStruct { icrash_breakpoints(false), mip_detect_symmetry(false), mip_allow_restart(false), + mip_old_analytic_centre_method(false); mip_max_nodes(0), mip_max_stall_nodes(0), mip_max_start_nodes(0), @@ -947,6 +949,11 @@ class HighsOptions : public HighsOptionsStruct { advanced, &mip_allow_restart, true); records.push_back(record_bool); + record_bool = new OptionRecordBool("mip_old_analytic_centre_method", + "Use origial or new analytic centre method in MIP", + advanced, &mip_old_analytic_centre_method, true); + records.push_back(record_bool); + record_int = new OptionRecordInt("mip_max_nodes", "MIP solver max number of nodes", advanced, &mip_max_nodes, 0, kHighsIInf, kHighsIInf); @@ -1042,10 +1049,9 @@ class HighsOptions : public HighsOptionsStruct { record_int = new OptionRecordInt( "mip_compute_analytic_centre", - "Compute analytic centre for MIP: 0 => no; 1 => original (default) 2 " - "=> true", + "Compute analytic centre for MIP: -1 => choose; 0 => off; 1 => on (default)", advanced, &mip_compute_analytic_centre, kMipAnalyticCentreCalulationMin, - kMipAnalyticCentreCalulationOriginal, kMipAnalyticCentreCalulationMax); + kMipAnalyticCentreCalulationOn, kMipAnalyticCentreCalulationMax); records.push_back(record_int); record_int = diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index 59377683d6..7a1984d4dd 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -302,12 +302,9 @@ void HighsMipSolverData::startAnalyticCenterComputation( taskGroup.spawn([&]() { // first check if the analytic centre computation should be cancelled, e.g. // due to early return in the root node evaluation - assert(mipsolver.options_mip_->mip_compute_analytic_centre >= - kMipAnalyticCentreCalulationOriginal); const bool ac_logging = !mipsolver.submip; // 2049 unset this Highs ipm; - if (mipsolver.options_mip_->mip_compute_analytic_centre == - kMipAnalyticCentreCalulationOriginal) { + if (mipsolver.options_mip_->mip_old_analytic_centre_method) { // Original calculation is just IPM with crossover off ipm.setOptionValue("solver", "ipm"); ipm.setOptionValue("run_crossover", kHighsOffString); diff --git a/src/mip/HighsPrimalHeuristics.cpp b/src/mip/HighsPrimalHeuristics.cpp index 8b147fd49d..851f01c700 100644 --- a/src/mip/HighsPrimalHeuristics.cpp +++ b/src/mip/HighsPrimalHeuristics.cpp @@ -138,7 +138,7 @@ bool HighsPrimalHeuristics::solveSubMip( // 2049 Set this ultimately // if (mipsolver.mipdata_->analyticCenterFailed) - submipoptions.mip_compute_analytic_centre = 0; + submipoptions.mip_compute_analytic_centre = kMipAnalyticCentreCalulationOff; // setup solver and run it HighsSolution solution; diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index 01db73686a..3d0870e00d 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -4453,11 +4453,12 @@ void HighsSimplexStats::report(FILE* file, std::string message, const HighsInt s this->row_DSE_density); } else if (style == HighsSolverStatsReportCsvHeader) { fprintf(file, - "valid,iteration_count,num_invert,last_factored_basis_num_el,last_invert_num_el," + "valid,col,row,nz,iteration_count,num_invert,last_factored_basis_num_el,last_invert_num_el," "col_aq_density,row_ep_density,row_ap_density,row_DSE_" "density,"); } else if (style == HighsSolverStatsReportCsvData) { - fprintf(file, "%d,%d,%d,%d,%d,%g,%g,%g,%g,", int(this->valid), + fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%g,%g,%g,%g,", int(this->valid), + int(this->num_col), int(this->num_row), int(this->num_nz), int(this->iteration_count), int(this->num_invert), int(this->last_factored_basis_num_el), int(this->last_invert_num_el), From 0861f045fed52ec981f1c85c8470b41e0b7c9bde Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 2 Dec 2024 23:09:52 +0000 Subject: [PATCH 31/40] Corrected IPX stats when kk1 has 0 iterations --- src/ipm/ipx/ipm.cc | 20 ++++++-------------- src/ipm/ipx/ipm.h | 2 +- src/ipm/ipx/lp_solver.cc | 6 ++++-- src/lp_data/HighsOptions.h | 2 +- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index 024a63ae93..ef97cd76d7 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -43,7 +43,7 @@ IPM::IPM(const Control& control) : control_(control) {} } } -void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { +void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info, const bool diag) { const Model& model = iterate->model(); const Int m = model.rows(); const Int n = model.cols(); @@ -87,8 +87,7 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { } if ((info->errflag = control_.InterruptCheck(info->iter)) != 0) break; - Int kktiter1 = -info_->kktiter1; - Int kktiter2 = -info_->kktiter2; + Int kktiter = diag ? -info_->kktiter1 : -info_->kktiter2; kkt->Factorize(iterate, info); if (info->errflag) break; @@ -105,20 +104,13 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { info->iter++; // Update IPX stats - - - - kktiter1 += info_->kktiter1; - kktiter2 += info_->kktiter2; - Int cr_type = kktiter1 > 0 ? 1 : 2; - Int kktiter = cr_type == 1 ? kktiter1 : kktiter2; - if (cr_type == 1) assert(kktiter2 == 0); - + kktiter += diag ? info_->kktiter1 : info_->kktiter2; + Int cr_type = diag ? 1 : 2; ipx_stats_->iteration_count++; ipx_stats_->cr_type.push_back(cr_type); ipx_stats_->cr_count.push_back(kktiter); - Int matrix_nz = cr_type == 1 ? 0 : kkt_->basis()->matrix_nz(); - Int invert_nz = cr_type == 1 ? 0 : kkt_->basis()->invert_nz(); + Int matrix_nz = diag ? 0 : kkt_->basis()->matrix_nz(); + Int invert_nz = diag ? 0 : kkt_->basis()->invert_nz(); ipx_stats_->factored_basis_num_el.push_back(matrix_nz); ipx_stats_->invert_num_el.push_back(invert_nz); // if (cr_type == 2) ipx_stats_->report(stdout, "IPM::Driver()"); diff --git a/src/ipm/ipx/ipm.h b/src/ipm/ipx/ipm.h index c5e937167b..ab5f2786b9 100644 --- a/src/ipm/ipx/ipm.h +++ b/src/ipm/ipx/ipm.h @@ -33,7 +33,7 @@ class IPM { // IPX_STATUS_no_progress if no progress over a number of iterations, // IPX_STATUS_time_limit if interrupted by time limit, // IPX_STATUS_failed if the KKT solver failed with info->errflag. - void Driver(KKTSolver* kkt, Iterate* iterate, Info* info); + void Driver(KKTSolver* kkt, Iterate* iterate, Info* info, const bool diag); Int maxiter() const { return maxiter_; } void maxiter(Int i) { maxiter_ = i; } diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index b2eda8b370..3464aaec3d 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -513,7 +513,8 @@ void LpSolver::RunInitialIPM(IPM& ipm) { } else { ipm.maxiter(std::min(switchiter, control_.ipm_maxiter())); } - ipm.Driver(&kkt, iterate_.get(), &info_); + const bool diag = true; + ipm.Driver(&kkt, iterate_.get(), &info_, diag); switch (info_.status_ipm) { case IPX_STATUS_optimal: // If the IPM reached its termination criterion in the initial @@ -580,7 +581,8 @@ void LpSolver::RunMainIPM(IPM& ipm) { Timer timer; ipm.maxiter(control_.ipm_maxiter()); kkt.maxiter(control_.cr2_maxiter()); - ipm.Driver(&kkt, iterate_.get(), &info_); + const bool diag = false; + ipm.Driver(&kkt, iterate_.get(), &info_, diag); info_.time_ipm2 = timer.Elapsed(); } diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index 0da3e040a5..fac32975aa 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -553,7 +553,7 @@ struct HighsOptionsStruct { icrash_breakpoints(false), mip_detect_symmetry(false), mip_allow_restart(false), - mip_old_analytic_centre_method(false); + mip_old_analytic_centre_method(false), mip_max_nodes(0), mip_max_stall_nodes(0), mip_max_start_nodes(0), From 3c980690339493694551e51cc91b0766ce85c817 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 2 Dec 2024 23:10:19 +0000 Subject: [PATCH 32/40] Corrected IPX stats when kk1 has 0 iterations; formatted --- check/TestLpSolvers.cpp | 3 +- src/Highs.h | 2 +- src/ipm/IpxWrapper.cpp | 87 +++++++++++++++---------------- src/lp_data/Highs.cpp | 16 +++--- src/lp_data/HighsInterface.cpp | 7 +-- src/lp_data/HighsLpSolverObject.h | 8 +-- src/lp_data/HighsOptions.h | 12 +++-- src/lp_data/HighsSolverStats.h | 7 ++- src/mip/HighsMipSolver.cpp | 10 ++-- src/mip/HighsMipSolverData.cpp | 10 ++-- src/mip/HighsMipSolverData.h | 2 +- src/mip/MipTimer.h | 5 +- src/simplex/HEkk.cpp | 38 +++++++------- src/simplex/HEkk.h | 6 +-- 14 files changed, 108 insertions(+), 105 deletions(-) diff --git a/check/TestLpSolvers.cpp b/check/TestLpSolvers.cpp index 522f13e01f..2a59938efe 100644 --- a/check/TestLpSolvers.cpp +++ b/check/TestLpSolvers.cpp @@ -729,7 +729,8 @@ TEST_CASE("ipx-stats", "[highs_lp_solver]") { REQUIRE(ipx_stats.num_row > 0); REQUIRE(ipx_stats.num_nz > 0); REQUIRE(ipx_stats.iteration_count > 0); - for (HighsInt iteration = 0; iteration < ipx_stats.iteration_count; iteration++) { + for (HighsInt iteration = 0; iteration < ipx_stats.iteration_count; + iteration++) { REQUIRE(ipx_stats.cr_count[iteration] > 0); if (ipx_stats.cr_type[iteration] == 2) { REQUIRE(ipx_stats.factored_basis_num_el[iteration] > 0); diff --git a/src/Highs.h b/src/Highs.h index 3d0039bcda..e4e06bc295 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1527,7 +1527,7 @@ class Highs { // // Invalidates all solver data in Highs class members by calling // invalidateModelStatus(), invalidateSolution(), invalidateBasis(), - // invalidateInfo(), invalidateEkk(), invalidateIis(), + // invalidateInfo(), invalidateEkk(), invalidateIis(), // invalidateSimplexStats() and invalidateIpxStats(); void invalidateUserSolverData(); // diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 6be7d9e0ef..1d96b61a1c 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -1089,90 +1089,85 @@ void reportSolveData(const HighsLogOptions& log_options, ipx_info.volume_increase); } -double HighsIpxStats::workEstimate() const { - return 0; -} +double HighsIpxStats::workEstimate() const { return 0; } -void HighsIpxStats::report(FILE* file, const std::string message, const HighsInt style) const { +void HighsIpxStats::report(FILE* file, const std::string message, + const HighsInt style) const { if (style == HighsSolverStatsReportPretty) { fprintf(file, "\nIpx stats: %s\n", message.c_str()); fprintf(file, " valid = %d\n", this->valid); - fprintf(file, " num_col = %d\n", - this->num_col); - fprintf(file, " num_row = %d\n", - this->num_row); - fprintf(file, " num_nz = %d\n", - this->num_nz); + fprintf(file, " num_col = %d\n", this->num_col); + fprintf(file, " num_row = %d\n", this->num_row); + fprintf(file, " num_nz = %d\n", this->num_nz); fprintf(file, " iteration_count = %d\n", this->iteration_count); if (this->iteration_count != HighsInt(cr_type.size())) printf("iteration_count = %d != %d = cr_type.size()\n", - int(this->iteration_count), - int(this->cr_type.size())); + int(this->iteration_count), int(this->cr_type.size())); if (this->iteration_count != HighsInt(cr_count.size())) printf("iteration_count = %d != %d = cr_count.size()\n", - int(this->iteration_count), - int(this->cr_count.size())); + int(this->iteration_count), int(this->cr_count.size())); if (this->iteration_count != HighsInt(invert_num_el.size())) printf("iteration_count = %d != %d = invert_num_el.size()\n", - int(this->iteration_count), - int(this->invert_num_el.size())); + int(this->iteration_count), int(this->invert_num_el.size())); if (this->iteration_count != HighsInt(factored_basis_num_el.size())) printf("iteration_count = %d != %d = factored_basis_num_el.size()\n", - int(this->iteration_count), - int(this->factored_basis_num_el.size())); + int(this->iteration_count), + int(this->factored_basis_num_el.size())); assert(this->iteration_count == HighsInt(this->cr_type.size())); assert(this->iteration_count == HighsInt(this->cr_count.size())); assert(this->iteration_count == HighsInt(this->invert_num_el.size())); - assert(this->iteration_count == HighsInt(this->factored_basis_num_el.size())); + assert(this->iteration_count == + HighsInt(this->factored_basis_num_el.size())); fprintf(file, " Iter type cr_count basisNz invertNz\n"); - //rintf(file, " dddd d ddddd ddddddd ddddddd\n"); + // rintf(file, " dddd d ddddd ddddddd ddddddd\n"); for (HighsInt iteration = 0; iteration < iteration_count; iteration++) { - fprintf(file, " %4d %1d %5d %7d %7d\n", - int(iteration), - int(this->cr_type[iteration]), - int(this->cr_count[iteration]), - int(this->factored_basis_num_el[iteration]), - int(this->invert_num_el[iteration])); + fprintf(file, " %4d %1d %5d %7d %7d\n", int(iteration), + int(this->cr_type[iteration]), int(this->cr_count[iteration]), + int(this->factored_basis_num_el[iteration]), + int(this->invert_num_el[iteration])); } } else if (style == HighsSolverStatsReportCsvHeader) { - fprintf(file, "valid,col,row,nz,iteration_count,cr_count,iteration_count, cr_count,matrix_nz,invert_nz,"); + fprintf(file, + "valid,col,row,nz,iteration_count,cr_count,iteration_count, " + "cr_count,matrix_nz,invert_nz,"); } else if (style == HighsSolverStatsReportCsvData) { HighsInt num_type1_iteration = 0; HighsInt num_type2_iteration = 0; double average_type1_cr_count = 0; double average_type2_cr_count = 0; - double average_type2_matrix_nz= 0; + double average_type2_matrix_nz = 0; double average_type2_invert_nz = 0; - for (HighsInt iteration = 0; iteration < this->iteration_count; iteration++) { + for (HighsInt iteration = 0; iteration < this->iteration_count; + iteration++) { if (this->cr_type[iteration] == 1) { - num_type1_iteration++; - average_type1_cr_count += this->cr_count[iteration]; + num_type1_iteration++; + average_type1_cr_count += this->cr_count[iteration]; } else { - num_type2_iteration++; - average_type2_cr_count += this->cr_count[iteration]; - average_type2_matrix_nz += this->factored_basis_num_el[iteration]; - average_type2_invert_nz += this->invert_num_el[iteration]; - } + num_type2_iteration++; + average_type2_cr_count += this->cr_count[iteration]; + average_type2_matrix_nz += this->factored_basis_num_el[iteration]; + average_type2_invert_nz += this->invert_num_el[iteration]; + } } - if (num_type1_iteration) - average_type1_cr_count /= (1.0 *num_type1_iteration); + if (num_type1_iteration) + average_type1_cr_count /= (1.0 * num_type1_iteration); if (num_type2_iteration) { - average_type2_cr_count /= (1.0 *num_type2_iteration); - average_type2_matrix_nz /= (1.0 *num_type2_iteration); - average_type2_invert_nz /= (1.0 *num_type2_iteration); - } + average_type2_cr_count /= (1.0 * num_type2_iteration); + average_type2_matrix_nz /= (1.0 * num_type2_iteration); + average_type2_invert_nz /= (1.0 * num_type2_iteration); + } fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,", int(this->valid), - int(this->num_col), int(this->num_row), int(this->num_nz), + int(this->num_col), int(this->num_row), int(this->num_nz), int(num_type1_iteration), int(average_type1_cr_count), - int(num_type2_iteration), int(average_type2_cr_count), - int(average_type2_matrix_nz), int(average_type2_invert_nz)); + int(num_type2_iteration), int(average_type2_cr_count), + int(average_type2_matrix_nz), int(average_type2_invert_nz)); } else { fprintf(file, "Unknown IPX stats report style of %d\n", int(style)); assert(123 == 456); } } - + void HighsIpxStats::initialise() { valid = false; iteration_count = 0; diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index 161587d944..7930537d88 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -1320,7 +1320,7 @@ HighsStatus Highs::solve() { solveLp(incumbent_lp, "Not presolved: solving the LP", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); - presolved_lp_simplex_stats_ = simplex_stats_; + presolved_lp_simplex_stats_ = simplex_stats_; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); if (return_status == HighsStatus::kError) @@ -1334,7 +1334,7 @@ HighsStatus Highs::solve() { solveLp(incumbent_lp, "Problem not reduced by presolve: solving the LP", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); - presolved_lp_simplex_stats_ = simplex_stats_; + presolved_lp_simplex_stats_ = simplex_stats_; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); if (return_status == HighsStatus::kError) @@ -2334,8 +2334,8 @@ HighsStatus Highs::setBasis(const HighsBasis& basis, HighsBasis modifiable_basis = basis; modifiable_basis.was_alien = true; HighsLpSolverObject solver_object(model_.lp_, modifiable_basis, solution_, - info_, ekk_instance_, ipx_stats_, callback_, - options_, timer_); + info_, ekk_instance_, ipx_stats_, + callback_, options_, timer_); HighsStatus return_status = formSimplexLpBasisAndFactor(solver_object); if (return_status != HighsStatus::kOk) return HighsStatus::kError; // Update the HiGHS basis @@ -3637,9 +3637,7 @@ void Highs::invalidateSimplexStats() { presolved_lp_simplex_stats_.initialise(); } -void Highs::invalidateIpxStats() { - ipx_stats_.initialise(); -} +void Highs::invalidateIpxStats() { ipx_stats_.initialise(); } HighsStatus Highs::completeSolutionFromDiscreteAssignment() { // Determine whether the current solution of a MIP is feasible and, @@ -3782,8 +3780,8 @@ HighsStatus Highs::completeSolutionFromDiscreteAssignment() { HighsStatus Highs::callSolveLp(HighsLp& lp, const string message) { HighsStatus return_status = HighsStatus::kOk; - HighsLpSolverObject solver_object(lp, basis_, solution_, info_, ekk_instance_, ipx_stats_, - callback_, options_, timer_); + HighsLpSolverObject solver_object(lp, basis_, solution_, info_, ekk_instance_, + ipx_stats_, callback_, options_, timer_); // Check that the model is column-wise assert(model_.lp_.a_matrix_.isColwise()); diff --git a/src/lp_data/HighsInterface.cpp b/src/lp_data/HighsInterface.cpp index 23359c862a..126dbe02f9 100644 --- a/src/lp_data/HighsInterface.cpp +++ b/src/lp_data/HighsInterface.cpp @@ -1435,8 +1435,8 @@ HighsStatus Highs::getBasicVariablesInterface(HighsInt* basic_variables) { // The LP has no invert to use, so have to set one up, but only // for the current basis, so return_value is the rank deficiency. HighsLpSolverObject solver_object(lp, basis_, solution_, info_, - ekk_instance_, ipx_stats_, - callback_, options_, timer_); + ekk_instance_, ipx_stats_, callback_, + options_, timer_); const bool only_from_known_basis = true; return_status = interpretCallStatus( options_.log_options, @@ -1945,7 +1945,8 @@ HighsStatus Highs::getPrimalRayInterface(bool& has_primal_ray, HighsStatus Highs::getRangingInterface() { HighsLpSolverObject solver_object(model_.lp_, basis_, solution_, info_, - ekk_instance_, ipx_stats_, callback_, options_, timer_); + ekk_instance_, ipx_stats_, callback_, + options_, timer_); solver_object.model_status_ = model_status_; return getRangingData(this->ranging_, solver_object); } diff --git a/src/lp_data/HighsLpSolverObject.h b/src/lp_data/HighsLpSolverObject.h index 1d493f4012..d47ca65e0f 100644 --- a/src/lp_data/HighsLpSolverObject.h +++ b/src/lp_data/HighsLpSolverObject.h @@ -21,15 +21,15 @@ class HighsLpSolverObject { public: HighsLpSolverObject(HighsLp& lp, HighsBasis& basis, HighsSolution& solution, - HighsInfo& highs_info, HEkk& ekk_instance, HighsIpxStats& ipx_stats, - HighsCallback& callback, HighsOptions& options, - HighsTimer& timer) + HighsInfo& highs_info, HEkk& ekk_instance, + HighsIpxStats& ipx_stats, HighsCallback& callback, + HighsOptions& options, HighsTimer& timer) : lp_(lp), basis_(basis), solution_(solution), highs_info_(highs_info), ekk_instance_(ekk_instance), - ipx_stats_(ipx_stats), + ipx_stats_(ipx_stats), callback_(callback), options_(options), timer_(timer) {} diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index fac32975aa..0543f547e5 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -553,7 +553,7 @@ struct HighsOptionsStruct { icrash_breakpoints(false), mip_detect_symmetry(false), mip_allow_restart(false), - mip_old_analytic_centre_method(false), + mip_old_analytic_centre_method(false), mip_max_nodes(0), mip_max_stall_nodes(0), mip_max_start_nodes(0), @@ -949,9 +949,10 @@ class HighsOptions : public HighsOptionsStruct { advanced, &mip_allow_restart, true); records.push_back(record_bool); - record_bool = new OptionRecordBool("mip_old_analytic_centre_method", - "Use origial or new analytic centre method in MIP", - advanced, &mip_old_analytic_centre_method, true); + record_bool = + new OptionRecordBool("mip_old_analytic_centre_method", + "Use origial or new analytic centre method in MIP", + advanced, &mip_old_analytic_centre_method, true); records.push_back(record_bool); record_int = new OptionRecordInt("mip_max_nodes", @@ -1049,7 +1050,8 @@ class HighsOptions : public HighsOptionsStruct { record_int = new OptionRecordInt( "mip_compute_analytic_centre", - "Compute analytic centre for MIP: -1 => choose; 0 => off; 1 => on (default)", + "Compute analytic centre for MIP: -1 => choose; 0 => off; 1 => on " + "(default)", advanced, &mip_compute_analytic_centre, kMipAnalyticCentreCalulationMin, kMipAnalyticCentreCalulationOn, kMipAnalyticCentreCalulationMax); records.push_back(record_int); diff --git a/src/lp_data/HighsSolverStats.h b/src/lp_data/HighsSolverStats.h index dd19b8f2fa..8ab61df127 100644 --- a/src/lp_data/HighsSolverStats.h +++ b/src/lp_data/HighsSolverStats.h @@ -38,9 +38,8 @@ struct HighsSimplexStats { double row_ap_density; double row_DSE_density; double workEstimate() const; - void report(FILE* file, - const std::string message = "", - const HighsInt style = HighsSolverStatsReportPretty) const; + void report(FILE* file, const std::string message = "", + const HighsInt style = HighsSolverStatsReportPretty) const; void initialise(const HighsInt iteration_count_ = 0); }; @@ -56,7 +55,7 @@ struct HighsIpxStats { std::vector invert_num_el; double workEstimate() const; void report(FILE* file, const std::string message = "", - const HighsInt style = HighsSolverStatsReportPretty) const; + const HighsInt style = HighsSolverStatsReportPretty) const; void initialise(); }; diff --git a/src/mip/HighsMipSolver.cpp b/src/mip/HighsMipSolver.cpp index 5bd5601a45..61555b5a72 100644 --- a/src/mip/HighsMipSolver.cpp +++ b/src/mip/HighsMipSolver.cpp @@ -166,12 +166,13 @@ void HighsMipSolver::run() { analysis_.mipTimerStop(kMipClockRunSetup); if (analysis_.analyse_mip_time && !submip) highsLogUser(options_mip_->log_options, HighsLogType::kInfo, - "MIP-Timing: %11.2g - completed setup\n", - timer_.read(timer_.total_clock)); + "MIP-Timing: %11.2g - completed setup\n", + timer_.read(timer_.total_clock)); if (!submip) { if (analysis_.analyse_mip_time) { highsLogUser(options_mip_->log_options, HighsLogType::kInfo, - "MIP-Timing: %11.2g - starting relaxation simplex solve\n", timer_.read()); + "MIP-Timing: %11.2g - starting relaxation simplex solve\n", + timer_.read()); analysis_.mipTimerStart(kMipClockRelaxationSimplexSolve); analysis_.mipTimerStart(kMipClockSimplexNoBasisSolveLp); } @@ -199,7 +200,8 @@ void HighsMipSolver::run() { analysis_.mipTimerStop(kMipClockSimplexNoBasisSolveLp); analysis_.mipTimerStop(kMipClockRelaxationSimplexSolve); highsLogUser(options_mip_->log_options, HighsLogType::kInfo, - "MIP-Timing: %11.2g - completed relaxation simplex solve\n", timer_.read()); + "MIP-Timing: %11.2g - completed relaxation simplex solve\n", + timer_.read()); } } restart: diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index 7a1984d4dd..f3d940688f 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -302,7 +302,7 @@ void HighsMipSolverData::startAnalyticCenterComputation( taskGroup.spawn([&]() { // first check if the analytic centre computation should be cancelled, e.g. // due to early return in the root node evaluation - const bool ac_logging = !mipsolver.submip; // 2049 unset this + const bool ac_logging = !mipsolver.submip; // 2049 unset this Highs ipm; if (mipsolver.options_mip_->mip_old_analytic_centre_method) { // Original calculation is just IPM with crossover off @@ -315,14 +315,15 @@ void HighsMipSolverData::startAnalyticCenterComputation( } ipm.setOptionValue("presolve", "off"); ipm.setOptionValue("output_flag", false); - ipm.setOptionValue("output_flag", !mipsolver.submip); // 2049 unset this ultimately + ipm.setOptionValue("output_flag", + !mipsolver.submip); // 2049 unset this ultimately ipm.setOptionValue("ipm_iteration_limit", 200); double time_available = std::max(mipsolver.options_mip_->time_limit - mipsolver.timer_.read(mipsolver.timer_.total_clock), 0.1); ipm.setOptionValue("time_limit", time_available); - ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this + ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this // ultimately // // cr1_iteration_limit is what's set internal to IPX to limit the @@ -349,7 +350,8 @@ void HighsMipSolverData::startAnalyticCenterComputation( lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); ipm.passModel(std::move(lpmodel)); - // if (!mipsolver.submip)ipm.writeModel(mipsolver.model_->model_name_ + "_ipm.mps"); + // if (!mipsolver.submip)ipm.writeModel(mipsolver.model_->model_name_ + + // "_ipm.mps"); if (ac_logging) num_analytic_centre_start++; double tt0 = mipsolver.timer_.read(mipsolver.timer_.total_clock); diff --git a/src/mip/HighsMipSolverData.h b/src/mip/HighsMipSolverData.h index aa95cf3ac4..291bf6fba0 100644 --- a/src/mip/HighsMipSolverData.h +++ b/src/mip/HighsMipSolverData.h @@ -91,7 +91,7 @@ struct HighsMipSolverData { HighsInt numRestartsRoot; HighsInt numCliqueEntriesAfterPresolve; HighsInt numCliqueEntriesAfterFirstPresolve; - HighsSimplexStats simplex_stats; + HighsSimplexStats simplex_stats; std::vector ARstart_; std::vector ARindex_; diff --git a/src/mip/MipTimer.h b/src/mip/MipTimer.h index 7f1d6476c3..fc927bdc42 100644 --- a/src/mip/MipTimer.h +++ b/src/mip/MipTimer.h @@ -97,7 +97,8 @@ class MipTimer { clock[kMipClockInit] = timer_pointer->clock_def("Initialise"); clock[kMipClockRunPresolve] = timer_pointer->clock_def("Run presolve"); clock[kMipClockRunSetup] = timer_pointer->clock_def("Run setup"); - clock[kMipClockRelaxationSimplexSolve] = timer_pointer->clock_def("Relaxation simplex solve"); + clock[kMipClockRelaxationSimplexSolve] = + timer_pointer->clock_def("Relaxation simplex solve"); clock[kMipClockTrivialHeuristics] = timer_pointer->clock_def("Trivial heuristics"); clock[kMipClockEvaluateRootNode] = @@ -257,7 +258,7 @@ class MipTimer { const std::vector mip_clock_list{kMipClockInit, kMipClockRunPresolve, kMipClockRunSetup, - kMipClockRelaxationSimplexSolve, + kMipClockRelaxationSimplexSolve, kMipClockTrivialHeuristics, kMipClockEvaluateRootNode, kMipClockPerformAging0, diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index 3d0870e00d..4ca24e9536 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -4429,16 +4429,14 @@ void HEkk::passSimplexStats(const HighsSimplexStats simplex_stats) { this->simplex_stats_ = simplex_stats; } -void HighsSimplexStats::report(FILE* file, std::string message, const HighsInt style) const { +void HighsSimplexStats::report(FILE* file, std::string message, + const HighsInt style) const { if (style == HighsSolverStatsReportPretty) { fprintf(file, "\nSimplex stats: %s\n", message.c_str()); fprintf(file, " valid = %d\n", this->valid); - fprintf(file, " num_col = %d\n", - this->num_col); - fprintf(file, " num_row = %d\n", - this->num_row); - fprintf(file, " num_nz = %d\n", - this->num_nz); + fprintf(file, " num_col = %d\n", this->num_col); + fprintf(file, " num_row = %d\n", this->num_row); + fprintf(file, " num_nz = %d\n", this->num_nz); fprintf(file, " iteration_count = %d\n", this->iteration_count); fprintf(file, " num_invert = %d\n", this->num_invert); @@ -4453,16 +4451,16 @@ void HighsSimplexStats::report(FILE* file, std::string message, const HighsInt s this->row_DSE_density); } else if (style == HighsSolverStatsReportCsvHeader) { fprintf(file, - "valid,col,row,nz,iteration_count,num_invert,last_factored_basis_num_el,last_invert_num_el," + "valid,col,row,nz,iteration_count,num_invert,last_factored_basis_" + "num_el,last_invert_num_el," "col_aq_density,row_ep_density,row_ap_density,row_DSE_" "density,"); } else if (style == HighsSolverStatsReportCsvData) { fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%g,%g,%g,%g,", int(this->valid), - int(this->num_col), int(this->num_row), int(this->num_nz), + int(this->num_col), int(this->num_row), int(this->num_nz), int(this->iteration_count), int(this->num_invert), int(this->last_factored_basis_num_el), - int(this->last_invert_num_el), - this->col_aq_density, + int(this->last_invert_num_el), this->col_aq_density, this->row_ep_density, this->row_ap_density, this->row_DSE_density); } else { fprintf(file, "Unknown simplex stats report style of %d\n", int(style)); @@ -4488,18 +4486,22 @@ void HighsSimplexStats::initialise(const HighsInt iteration_count_) { double HighsSimplexStats::workEstimate() const { double work = 0; // INVERT - work += num_invert*(2*last_factored_basis_num_el + 2*num_row); + work += num_invert * (2 * last_factored_basis_num_el + 2 * num_row); // Compute primal - work += num_invert*(last_invert_num_el + num_nz-last_factored_basis_num_el); + work += + num_invert * (last_invert_num_el + num_nz - last_factored_basis_num_el); // Compute dual - work += num_invert*(last_invert_num_el + num_nz-last_factored_basis_num_el); + work += + num_invert * (last_invert_num_el + num_nz - last_factored_basis_num_el); // BTRAN - work += iteration_count*num_row*row_ep_density; + work += iteration_count * num_row * row_ep_density; // PRICE - work += iteration_count*row_ep_density*(num_nz-last_factored_basis_num_el) + num_col*row_ap_density; + work += + iteration_count * row_ep_density * (num_nz - last_factored_basis_num_el) + + num_col * row_ap_density; // FTRAN - work += iteration_count*num_row*col_aq_density; + work += iteration_count * num_row * col_aq_density; // FTRAN_DSE - work += iteration_count*num_row*row_DSE_density; + work += iteration_count * num_row * row_DSE_density; return work; } diff --git a/src/simplex/HEkk.h b/src/simplex/HEkk.h index efbd20c515..e29226adc4 100644 --- a/src/simplex/HEkk.h +++ b/src/simplex/HEkk.h @@ -160,9 +160,9 @@ class HEkk { const HighsSimplexStats& getSimplexStats() const { return simplex_stats_; } void passSimplexStats(const HighsSimplexStats simplex_stats); void initialiseSimplexStats() { simplex_stats_.initialise(iteration_count_); } - void reportSimplexStats(FILE* file, - const std::string message = "", - const HighsInt style = HighsSolverStatsReportPretty) const { + void reportSimplexStats( + FILE* file, const std::string message = "", + const HighsInt style = HighsSolverStatsReportPretty) const { simplex_stats_.report(file, message, style); } From 593ec7b4f785c5ff8b3ed633d17bc00cc61b3da0 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 2 Dec 2024 23:42:51 +0000 Subject: [PATCH 33/40] Suppressing initial_root_node_solve --- src/mip/HighsMipSolver.cpp | 3 ++- src/mip/HighsMipSolverData.cpp | 14 +++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/mip/HighsMipSolver.cpp b/src/mip/HighsMipSolver.cpp index 61555b5a72..cf5ab3f7a2 100644 --- a/src/mip/HighsMipSolver.cpp +++ b/src/mip/HighsMipSolver.cpp @@ -168,7 +168,8 @@ void HighsMipSolver::run() { highsLogUser(options_mip_->log_options, HighsLogType::kInfo, "MIP-Timing: %11.2g - completed setup\n", timer_.read(timer_.total_clock)); - if (!submip) { + const bool initial_root_node_solve = false; + if (!submip && initial_root_node_solve) { if (analysis_.analyse_mip_time) { highsLogUser(options_mip_->log_options, HighsLogType::kInfo, "MIP-Timing: %11.2g - starting relaxation simplex solve\n", diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index f3d940688f..e3fee8fe8a 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -302,7 +302,7 @@ void HighsMipSolverData::startAnalyticCenterComputation( taskGroup.spawn([&]() { // first check if the analytic centre computation should be cancelled, e.g. // due to early return in the root node evaluation - const bool ac_logging = !mipsolver.submip; // 2049 unset this + const bool ac_logging = false; //! mipsolver.submip; // 2049 unset this Highs ipm; if (mipsolver.options_mip_->mip_old_analytic_centre_method) { // Original calculation is just IPM with crossover off @@ -315,15 +315,16 @@ void HighsMipSolverData::startAnalyticCenterComputation( } ipm.setOptionValue("presolve", "off"); ipm.setOptionValue("output_flag", false); - ipm.setOptionValue("output_flag", - !mipsolver.submip); // 2049 unset this ultimately + // ipm.setOptionValue("output_flag", !mipsolver.submip); // 2049 unset + // this ultimately ipm.setOptionValue("ipm_iteration_limit", 200); double time_available = std::max(mipsolver.options_mip_->time_limit - mipsolver.timer_.read(mipsolver.timer_.total_clock), 0.1); ipm.setOptionValue("time_limit", time_available); - ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset this + // ipm.setOptionValue("kkt_logging", !mipsolver.submip); // 2049 unset + // this // ultimately // // cr1_iteration_limit is what's set internal to IPX to limit the @@ -344,7 +345,10 @@ void HighsMipSolverData::startAnalyticCenterComputation( // 2049 Set this ultimately // ipm.setOptionValue("cr2_iteration_limit", cr2_iteration_limit); - if (!mipsolver.submip) this->simplex_stats.report(stdout); + // if (!mipsolver.submip) this->simplex_stats.report(stdout);2049 unset + // this + // ultimately + // ipm.passSimplexStats(this->simplex_stats); HighsLp lpmodel(*mipsolver.model_); lpmodel.col_cost_.assign(lpmodel.num_col_, 0.0); From a3d3fb53cd1ec194eab425d804db140e8eb48c38 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Thu, 5 Dec 2024 11:17:35 +0000 Subject: [PATCH 34/40] Now producing positive terms in simplex cost function --- src/lp_data/HighsSolverStats.h | 14 ++++++++++++ src/simplex/HEkk.cpp | 41 +++++++++++++++++----------------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/lp_data/HighsSolverStats.h b/src/lp_data/HighsSolverStats.h index 8ab61df127..a1186185c3 100644 --- a/src/lp_data/HighsSolverStats.h +++ b/src/lp_data/HighsSolverStats.h @@ -24,6 +24,19 @@ enum HighsSolverStatsReport { HighsSolverStatsReportCsvData }; +enum HighsSimplexWorkTerm { + HighsSimplexWorkTermInvertNumRow = 0, + HighsSimplexWorkTermInvertNumNz, + HighsSimplexWorkTermComputePD, + HighsSimplexWorkTermBtran, + HighsSimplexWorkTermPrice, + HighsSimplexWorkTermFtran, + HighsSimplexWorkTermFtranDse, + HighsSimplexWorkTermCount +}; + +const std::vector kSimplexWorkCoefficients = {1.0,2.0,3.0,4.0,5.0,6.0,7.0}; + struct HighsSimplexStats { bool valid; HighsInt num_col; @@ -37,6 +50,7 @@ struct HighsSimplexStats { double row_ep_density; double row_ap_density; double row_DSE_density; + void workTerms(double* terms) const; double workEstimate() const; void report(FILE* file, const std::string message = "", const HighsInt style = HighsSolverStatsReportPretty) const; diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index 4ca24e9536..59a4a68be4 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -4483,25 +4483,26 @@ void HighsSimplexStats::initialise(const HighsInt iteration_count_) { row_DSE_density = 0; } +void HighsSimplexStats::workTerms(double* terms) const { + const double nonbasic_nz = double(this->num_nz + this->num_row - this->last_factored_basis_num_el); + terms[HighsSimplexWorkTermInvertNumRow] = double(this->num_invert) * double(this->num_row); + terms[HighsSimplexWorkTermInvertNumNz] = double(this->num_invert) * this->last_factored_basis_num_el; + terms[HighsSimplexWorkTermComputePD] = double(this->num_invert) * double(this->last_invert_num_el + nonbasic_nz); + terms[HighsSimplexWorkTermBtran] = double(this->iteration_count) * double(this->last_invert_num_el) * this->row_ep_density; + terms[HighsSimplexWorkTermPrice] = double(this->iteration_count) * nonbasic_nz * this->row_ep_density; + terms[HighsSimplexWorkTermFtran] = double(this->iteration_count) * double(this->last_invert_num_el) * this->col_aq_density; + terms[HighsSimplexWorkTermFtranDse] = double(this->iteration_count) * double(this->last_invert_num_el) * this->row_DSE_density; +} + + double HighsSimplexStats::workEstimate() const { - double work = 0; - // INVERT - work += num_invert * (2 * last_factored_basis_num_el + 2 * num_row); - // Compute primal - work += - num_invert * (last_invert_num_el + num_nz - last_factored_basis_num_el); - // Compute dual - work += - num_invert * (last_invert_num_el + num_nz - last_factored_basis_num_el); - // BTRAN - work += iteration_count * num_row * row_ep_density; - // PRICE - work += - iteration_count * row_ep_density * (num_nz - last_factored_basis_num_el) + - num_col * row_ap_density; - // FTRAN - work += iteration_count * num_row * col_aq_density; - // FTRAN_DSE - work += iteration_count * num_row * row_DSE_density; - return work; + double* terms = new double[HighsSimplexWorkTermCount]; + this->workTerms(terms); + double work = 0; + for (HighsInt iX = 0; iX < HighsSimplexWorkTermCount; iX++) { + assert(terms[iX]>0); + work += terms[iX] * kSimplexWorkCoefficients[iX]; + } + delete[] terms; + return work; } From 42e2b0590b25cda560440d13bca93b01e9deaebd Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 9 Dec 2024 10:55:55 +0000 Subject: [PATCH 35/40] Incorporate simplex/IPX/crossover times in solver stats --- src/Highs.h | 4 +- src/ipm/IpxWrapper.cpp | 88 ++++++++++++++++++++++------------ src/ipm/ipx/ipm.cc | 4 +- src/lp_data/HighsSolverStats.h | 24 +++++++++- 4 files changed, 84 insertions(+), 36 deletions(-) diff --git a/src/Highs.h b/src/Highs.h index e4e06bc295..8d22e4fb29 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1236,10 +1236,10 @@ class Highs { this->simplex_stats_ = simplex_stats; } - const HighsIpxStats& getIpxStats() const { return ipx_stats_; } + const HighsIpxStats& getIpxStats() { return ipx_stats_; } void reportIpxStats( - FILE* file, const HighsInt style = HighsSolverStatsReportPretty) const { + FILE* file, const HighsInt style = HighsSolverStatsReportPretty) { ipx_stats_.report(file, "Original LP", style); } diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 1d96b61a1c..9d3c831dc7 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -1089,10 +1089,56 @@ void reportSolveData(const HighsLogOptions& log_options, ipx_info.volume_increase); } -double HighsIpxStats::workEstimate() const { return 0; } +void HighsIpxStats::workTerms(double* terms) { + const double nonbasic_nz = double(this->num_nz + this->num_row - this->average_type2_matrix_nz); + terms[HighsIpxWorkTermCr1IterNumRow] = double(this->num_type1_iteration) * average_type1_cr_count * double(this->num_row); + terms[HighsIpxWorkTermCr1IterNumNz] = double(this->num_type1_iteration) * average_type1_cr_count * double(this->num_nz); + terms[HighsIpxWorkTermCr2IterNumRow] = double(this->num_type2_iteration) * average_type2_cr_count * double(this->num_row); + terms[HighsIpxWorkTermCr2IterNumNz] = double(this->num_type2_iteration) * average_type2_cr_count * (nonbasic_nz + average_type2_invert_nz); +} + +double HighsIpxStats::workEstimate() { + double* terms = new double[HighsIpxWorkTermCount]; + this->workTerms(terms); + double work = 0; + for (HighsInt iX = 0; iX < HighsIpxWorkTermCount; iX++) { + assert(terms[iX]>0); + work += terms[iX] * kIpxWorkCoefficients[iX]; + } + delete[] terms; + return work; +} + +void HighsIpxStats::averages() { + num_type1_iteration = 0; + num_type2_iteration = 0; + average_type1_cr_count = 0; + average_type2_cr_count = 0; + average_type2_matrix_nz = 0; + average_type2_invert_nz = 0; + for (HighsInt iteration = 0; iteration < this->iteration_count; + iteration++) { + if (this->cr_type[iteration] == 1) { + num_type1_iteration++; + average_type1_cr_count += this->cr_count[iteration]; + } else { + num_type2_iteration++; + average_type2_cr_count += this->cr_count[iteration]; + average_type2_matrix_nz += this->factored_basis_num_el[iteration]; + average_type2_invert_nz += this->invert_num_el[iteration]; + } + } + if (num_type1_iteration) + average_type1_cr_count /= (1.0 * num_type1_iteration); + if (num_type2_iteration) { + average_type2_cr_count /= (1.0 * num_type2_iteration); + average_type2_matrix_nz /= (1.0 * num_type2_iteration); + average_type2_invert_nz /= (1.0 * num_type2_iteration); + } +} void HighsIpxStats::report(FILE* file, const std::string message, - const HighsInt style) const { + const HighsInt style) { if (style == HighsSolverStatsReportPretty) { fprintf(file, "\nIpx stats: %s\n", message.c_str()); fprintf(file, " valid = %d\n", this->valid); @@ -1132,36 +1178,12 @@ void HighsIpxStats::report(FILE* file, const std::string message, "valid,col,row,nz,iteration_count,cr_count,iteration_count, " "cr_count,matrix_nz,invert_nz,"); } else if (style == HighsSolverStatsReportCsvData) { - HighsInt num_type1_iteration = 0; - HighsInt num_type2_iteration = 0; - double average_type1_cr_count = 0; - double average_type2_cr_count = 0; - double average_type2_matrix_nz = 0; - double average_type2_invert_nz = 0; - for (HighsInt iteration = 0; iteration < this->iteration_count; - iteration++) { - if (this->cr_type[iteration] == 1) { - num_type1_iteration++; - average_type1_cr_count += this->cr_count[iteration]; - } else { - num_type2_iteration++; - average_type2_cr_count += this->cr_count[iteration]; - average_type2_matrix_nz += this->factored_basis_num_el[iteration]; - average_type2_invert_nz += this->invert_num_el[iteration]; - } - } - if (num_type1_iteration) - average_type1_cr_count /= (1.0 * num_type1_iteration); - if (num_type2_iteration) { - average_type2_cr_count /= (1.0 * num_type2_iteration); - average_type2_matrix_nz /= (1.0 * num_type2_iteration); - average_type2_invert_nz /= (1.0 * num_type2_iteration); - } + this->averages(); fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,", int(this->valid), int(this->num_col), int(this->num_row), int(this->num_nz), - int(num_type1_iteration), int(average_type1_cr_count), - int(num_type2_iteration), int(average_type2_cr_count), - int(average_type2_matrix_nz), int(average_type2_invert_nz)); + int(this->num_type1_iteration), int(this->average_type1_cr_count), + int(this->num_type2_iteration), int(this->average_type2_cr_count), + int(this->average_type2_matrix_nz), int(this->average_type2_invert_nz)); } else { fprintf(file, "Unknown IPX stats report style of %d\n", int(style)); assert(123 == 456); @@ -1179,4 +1201,10 @@ void HighsIpxStats::initialise() { cr_count.clear(); factored_basis_num_el.clear(); invert_num_el.clear(); + num_type1_iteration = 0; + num_type2_iteration = 0; + average_type1_cr_count = 0; + average_type2_cr_count = 0; + average_type2_matrix_nz = 0; + average_type2_invert_nz = 0; } diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index ef97cd76d7..fca4730476 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -98,8 +98,8 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info, const bool diag) if (info->errflag) break; MakeStep(step); - HighsSimplexStats simplex_stats = control_.simplexStats(); - double simplex_work_measure = simplex_stats.workEstimate(); + // HighsSimplexStats simplex_stats = control_.simplexStats(); + // double simplex_work_measure = simplex_stats.workEstimate(); // info->iter++; diff --git a/src/lp_data/HighsSolverStats.h b/src/lp_data/HighsSolverStats.h index a1186185c3..72acf466ce 100644 --- a/src/lp_data/HighsSolverStats.h +++ b/src/lp_data/HighsSolverStats.h @@ -35,8 +35,20 @@ enum HighsSimplexWorkTerm { HighsSimplexWorkTermCount }; +const std::vector kSimplexWorkNames = {"InvertNumRow", "InvertNumNz", "ComputePD", "Btran", "Price", "Ftran", "FtranDse"}; const std::vector kSimplexWorkCoefficients = {1.0,2.0,3.0,4.0,5.0,6.0,7.0}; +enum HighsIpxWorkTerm { + HighsIpxWorkTermCr1IterNumRow = 0, + HighsIpxWorkTermCr1IterNumNz, + HighsIpxWorkTermCr2IterNumRow, + HighsIpxWorkTermCr2IterNumNz, + HighsIpxWorkTermCount +}; + +const std::vector kIpxWorkNames = {"CR1IterNumRow", "CR1IterNumNz", "CR2IterNumRow", "CR2IterNumNz"}; +const std::vector kIpxWorkCoefficients = {1.0, 2.0, 3.0, 4.0}; + struct HighsSimplexStats { bool valid; HighsInt num_col; @@ -67,9 +79,17 @@ struct HighsIpxStats { std::vector cr_count; std::vector factored_basis_num_el; std::vector invert_num_el; - double workEstimate() const; + HighsInt num_type1_iteration; + HighsInt num_type2_iteration; + double average_type1_cr_count; + double average_type2_cr_count; + double average_type2_matrix_nz; + double average_type2_invert_nz; + void workTerms(double* terms); + double workEstimate(); + void averages(); void report(FILE* file, const std::string message = "", - const HighsInt style = HighsSolverStatsReportPretty) const; + const HighsInt style = HighsSolverStatsReportPretty); void initialise(); }; From d77061c3b27a3e7b3ea47ae23cf23cdfbb8b1495 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 9 Dec 2024 11:25:58 +0000 Subject: [PATCH 36/40] Incorporated simplex time in solver stats --- src/ipm/IpxWrapper.cpp | 11 ++++++++--- src/lp_data/Highs.cpp | 6 ++++++ src/lp_data/HighsSolverStats.h | 3 +++ src/simplex/HEkk.cpp | 10 +++++++--- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 9d3c831dc7..5fe6aa571c 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -185,6 +185,7 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, kHighsAnalysisLevelSolverSummaryData & options.highs_analysis_level; // Get solver and solution information. ipx_stats = lps.GetIpxStats(); + ipx_stats.averages(); ipx_stats.valid = true; // Struct ipx_info defined in ipx/ipx_info.h const ipx::Info ipx_info = lps.GetInfo(); @@ -1173,17 +1174,19 @@ void HighsIpxStats::report(FILE* file, const std::string message, int(this->factored_basis_num_el[iteration]), int(this->invert_num_el[iteration])); } + fprintf(file, "IPM time = %g; crossover time = %g\n", this->ipm_time, this->crossover_time); } else if (style == HighsSolverStatsReportCsvHeader) { fprintf(file, "valid,col,row,nz,iteration_count,cr_count,iteration_count, " - "cr_count,matrix_nz,invert_nz,"); + "cr_count,matrix_nz,invert_nz,ipm_time,crossover_time,"); } else if (style == HighsSolverStatsReportCsvData) { this->averages(); - fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,", int(this->valid), + fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%g,%g,", int(this->valid), int(this->num_col), int(this->num_row), int(this->num_nz), int(this->num_type1_iteration), int(this->average_type1_cr_count), int(this->num_type2_iteration), int(this->average_type2_cr_count), - int(this->average_type2_matrix_nz), int(this->average_type2_invert_nz)); + int(this->average_type2_matrix_nz), int(this->average_type2_invert_nz), + this->ipm_time, this->crossover_time); } else { fprintf(file, "Unknown IPX stats report style of %d\n", int(style)); assert(123 == 456); @@ -1207,4 +1210,6 @@ void HighsIpxStats::initialise() { average_type2_cr_count = 0; average_type2_matrix_nz = 0; average_type2_invert_nz = 0; + ipm_time = 0; + crossover_time = 0; } diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index 7930537d88..7d5c101aaf 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -1276,6 +1276,7 @@ HighsStatus Highs::solve() { "Solving LP without presolve, or with basis, or unconstrained", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); + simplex_stats_.simplex_time = this_solve_original_lp_time; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); if (return_status == HighsStatus::kError) @@ -1320,6 +1321,7 @@ HighsStatus Highs::solve() { solveLp(incumbent_lp, "Not presolved: solving the LP", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); + simplex_stats_.simplex_time = this_solve_original_lp_time; presolved_lp_simplex_stats_ = simplex_stats_; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); @@ -1334,6 +1336,7 @@ HighsStatus Highs::solve() { solveLp(incumbent_lp, "Problem not reduced by presolve: solving the LP", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); + simplex_stats_.simplex_time = this_solve_original_lp_time; presolved_lp_simplex_stats_ = simplex_stats_; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); @@ -1384,6 +1387,7 @@ HighsStatus Highs::solve() { solveLp(reduced_lp, "Solving the presolved LP", this_solve_presolved_lp_time); presolved_lp_simplex_stats_ = this->ekk_instance_.getSimplexStats(); + presolved_lp_simplex_stats_.simplex_time = this_solve_presolved_lp_time; if (ekk_instance_.status_.initialised_for_solve) { // Record the pivot threshold resulting from solving the presolved LP // with simplex @@ -1449,6 +1453,7 @@ HighsStatus Highs::solve() { "to determine infeasible or unbounded", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); + simplex_stats_.simplex_time = this_solve_original_lp_time; // Recover the options options_ = save_options; if (return_status == HighsStatus::kError) @@ -1587,6 +1592,7 @@ HighsStatus Highs::solve() { "Solving the original LP from the solution after postsolve", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); + simplex_stats_.simplex_time = this_solve_original_lp_time; // Determine the iteration count postsolve_iteration_count += info_.simplex_iteration_count; return_status = diff --git a/src/lp_data/HighsSolverStats.h b/src/lp_data/HighsSolverStats.h index 72acf466ce..85950a48e3 100644 --- a/src/lp_data/HighsSolverStats.h +++ b/src/lp_data/HighsSolverStats.h @@ -62,6 +62,7 @@ struct HighsSimplexStats { double row_ep_density; double row_ap_density; double row_DSE_density; + double simplex_time; void workTerms(double* terms) const; double workEstimate() const; void report(FILE* file, const std::string message = "", @@ -85,6 +86,8 @@ struct HighsIpxStats { double average_type2_cr_count; double average_type2_matrix_nz; double average_type2_invert_nz; + double ipm_time; + double crossover_time; void workTerms(double* terms); double workEstimate(); void averages(); diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index 59a4a68be4..daea75e843 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -4449,19 +4449,22 @@ void HighsSimplexStats::report(FILE* file, std::string message, fprintf(file, " row_ap_density = %g\n", this->row_ap_density); fprintf(file, " row_DSE_density = %g\n", this->row_DSE_density); + fprintf(file, " dimplex time = = %g\n", + this->simplex_time); } else if (style == HighsSolverStatsReportCsvHeader) { fprintf(file, "valid,col,row,nz,iteration_count,num_invert,last_factored_basis_" "num_el,last_invert_num_el," "col_aq_density,row_ep_density,row_ap_density,row_DSE_" - "density,"); + "density,simplex_time,"); } else if (style == HighsSolverStatsReportCsvData) { - fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%g,%g,%g,%g,", int(this->valid), + fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%g,%g,%g,%g,%g,", int(this->valid), int(this->num_col), int(this->num_row), int(this->num_nz), int(this->iteration_count), int(this->num_invert), int(this->last_factored_basis_num_el), int(this->last_invert_num_el), this->col_aq_density, - this->row_ep_density, this->row_ap_density, this->row_DSE_density); + this->row_ep_density, this->row_ap_density, this->row_DSE_density, + this->simplex_time); } else { fprintf(file, "Unknown simplex stats report style of %d\n", int(style)); assert(123 == 456); @@ -4481,6 +4484,7 @@ void HighsSimplexStats::initialise(const HighsInt iteration_count_) { row_ep_density = 0; row_ap_density = 0; row_DSE_density = 0; + simplex_time = 0; } void HighsSimplexStats::workTerms(double* terms) const { From 5d5393cde24f2587afba5f899955b009314566a1 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Mon, 9 Dec 2024 12:08:56 +0000 Subject: [PATCH 37/40] Gather new stats --- src/ipm/ipx/lp_solver.cc | 4 ++++ src/simplex/HApp.h | 24 +++++++++++++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index 3464aaec3d..432c43d41b 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -55,7 +55,9 @@ Int LpSolver::Solve() { control_.OpenLogfile(); control_.hLog("IPX version 1.0\n"); try { + ipx_stats_.ipm_time = -control_.Elapsed(); InteriorPointSolve(); + ipx_stats_.ipm_time += control_.Elapsed(); const bool run_crossover_on = control_.run_crossover() == 1; const bool run_crossover_choose = control_.run_crossover() == -1; const bool run_crossover_not_off = run_crossover_choose || run_crossover_on; @@ -73,8 +75,10 @@ Int LpSolver::Solve() { } else { assert(run_crossover_on || run_crossover_choose); } + ipx_stats_.crossover_time = -control_.Elapsed(); BuildCrossoverStartingPoint(); RunCrossover(); + ipx_stats_.crossover_time += control_.Elapsed(); } if (basis_) { info_.ftran_sparse = basis_->frac_ftran_sparse(); diff --git a/src/simplex/HApp.h b/src/simplex/HApp.h index b8de2a3f59..42ec51a53d 100644 --- a/src/simplex/HApp.h +++ b/src/simplex/HApp.h @@ -183,6 +183,7 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { // basis are taken from the unscaled solution of the scaled LP. bool solve_unscaled_lp = false; bool solved_unscaled_lp = false; + bool refine_solution = false; if (!incumbent_lp.scale_.has_scaling) { // // Solve the unscaled LP with unscaled NLA @@ -206,7 +207,6 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { } else { // Indicate that there is no (current) need to refine the solution // by solving the unscaled LP with scaled NLA - bool refine_solution = false; if (options.simplex_unscaled_solution_strategy == kSimplexUnscaledSolutionStrategyNone || options.simplex_unscaled_solution_strategy == @@ -334,22 +334,24 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { if (ekk_instance.proofOfPrimalInfeasibility()) solve_unscaled_lp = false; } if (solve_unscaled_lp) { + if (refine_solution) + printf("GrepSolverStats: Model %s requires refinement due to num/max/sum primal " + "(%" HIGHSINT_FORMAT "/%g/%g) and dual (%" HIGHSINT_FORMAT + "/%g/%g) " + "unscaled infeasibilities\n", + incumbent_lp.model_name_.c_str(), + highs_info.num_primal_infeasibilities, + highs_info.max_primal_infeasibility, + highs_info.sum_primal_infeasibilities, + highs_info.num_dual_infeasibilities, + highs_info.max_dual_infeasibility, + highs_info.sum_dual_infeasibilities); // Save options/strategies that may be changed HighsInt simplex_strategy = options.simplex_strategy; double dual_simplex_cost_perturbation_multiplier = options.dual_simplex_cost_perturbation_multiplier; HighsInt simplex_dual_edge_weight_strategy = ekk_info.dual_edge_weight_strategy; - // #1865 exposed that this should not be - // HighsModelStatus::kObjectiveBound, but - // HighsModelStatus::kObjectiveTarget, since if the latter is - // the model status for the scaled LP, any primal - // infeasibilities should be small, but must be cleaned up - // before (hopefully) a few phase 2 primal simplex iterations - // are required to attain the target for the unscaled LP - // - // In #1865, phase 2 primal simplex was forced with large primal - // infeasibilities if (num_unscaled_primal_infeasibilities == 0 || scaled_model_status == HighsModelStatus::kObjectiveTarget) { // Only dual infeasibilities, or objective target reached (in From 9546d039b117ac61466cdd8d266e9a0b6c035560 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Tue, 10 Dec 2024 21:42:44 +0000 Subject: [PATCH 38/40] Introduce separate CR1 and CR2 timing --- src/ipm/IpxWrapper.cpp | 18 +++++++++++++----- src/simplex/HEkk.cpp | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 5fe6aa571c..fa2ea9746d 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -1166,15 +1166,23 @@ void HighsIpxStats::report(FILE* file, const std::string message, assert(this->iteration_count == HighsInt(this->invert_num_el.size())); assert(this->iteration_count == HighsInt(this->factored_basis_num_el.size())); - fprintf(file, " Iter type cr_count basisNz invertNz\n"); - // rintf(file, " dddd d ddddd ddddddd ddddddd\n"); - for (HighsInt iteration = 0; iteration < iteration_count; iteration++) { + if (iteration_count>0) + fprintf(file, " Iter type cr_count basisNz invertNz\n"); + // printf(file, " dddd d ddddd ddddddd ddddddd\n"); + for (HighsInt iteration = 0; iteration < iteration_count; iteration++) fprintf(file, " %4d %1d %5d %7d %7d\n", int(iteration), int(this->cr_type[iteration]), int(this->cr_count[iteration]), int(this->factored_basis_num_el[iteration]), int(this->invert_num_el[iteration])); - } - fprintf(file, "IPM time = %g; crossover time = %g\n", this->ipm_time, this->crossover_time); + fprintf(file, " num_nz = %d\n", this->num_nz); + fprintf(file, " Type 1 iteration = %d\n", this->num_type1_iteration); + fprintf(file, " mean CR count = %g\n", this->average_type1_cr_count); + fprintf(file, " Type 2 iteration = %d\n", this->num_type2_iteration); + fprintf(file, " mean CR count = %g\n", this->average_type2_cr_count); + fprintf(file, " mean CR matrix nz = %g\n", this->average_type2_matrix_nz); + fprintf(file, " mean CR INVERT nz = %g\n", this->average_type2_invert_nz); + fprintf(file, "IPM time = %g\n", this->ipm_time); + fprintf(file, "Crossover time = %g\n", this->crossover_time); } else if (style == HighsSolverStatsReportCsvHeader) { fprintf(file, "valid,col,row,nz,iteration_count,cr_count,iteration_count, " diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index daea75e843..f3897fb1c7 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -4449,7 +4449,7 @@ void HighsSimplexStats::report(FILE* file, std::string message, fprintf(file, " row_ap_density = %g\n", this->row_ap_density); fprintf(file, " row_DSE_density = %g\n", this->row_DSE_density); - fprintf(file, " dimplex time = = %g\n", + fprintf(file, " simplex time = = %g\n", this->simplex_time); } else if (style == HighsSolverStatsReportCsvHeader) { fprintf(file, From 0f68c8e119393ff16f318ac6774937f413387f97 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Wed, 11 Dec 2024 09:30:35 +0000 Subject: [PATCH 39/40] Added Type1, Type2 and Basis0 timing --- src/ipm/IpxWrapper.cpp | 16 +++++++++++----- src/ipm/ipx/lp_solver.cc | 6 ++++++ src/lp_data/HighsSolverStats.h | 3 +++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index fa2ea9746d..c4fcbeef4a 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -1181,20 +1181,23 @@ void HighsIpxStats::report(FILE* file, const std::string message, fprintf(file, " mean CR count = %g\n", this->average_type2_cr_count); fprintf(file, " mean CR matrix nz = %g\n", this->average_type2_matrix_nz); fprintf(file, " mean CR INVERT nz = %g\n", this->average_type2_invert_nz); - fprintf(file, "IPM time = %g\n", this->ipm_time); - fprintf(file, "Crossover time = %g\n", this->crossover_time); + fprintf(file, " Type 1 time = %g\n", this->type1_time); + fprintf(file, " Starting basis time = %g\n", this->basis0_time); + fprintf(file, " Type 2 time = %g\n", this->type2_time); + fprintf(file, " IPM time = %g\n", this->ipm_time); + fprintf(file, " Crossover time = %g\n", this->crossover_time); } else if (style == HighsSolverStatsReportCsvHeader) { fprintf(file, "valid,col,row,nz,iteration_count,cr_count,iteration_count, " - "cr_count,matrix_nz,invert_nz,ipm_time,crossover_time,"); + "cr_count,matrix_nz,invert_nz,type1_time,basis0_time,type2_time,ipm_time,crossover_time,"); } else if (style == HighsSolverStatsReportCsvData) { this->averages(); - fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%g,%g,", int(this->valid), + fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%g,%g,%g,%g,%g,", int(this->valid), int(this->num_col), int(this->num_row), int(this->num_nz), int(this->num_type1_iteration), int(this->average_type1_cr_count), int(this->num_type2_iteration), int(this->average_type2_cr_count), int(this->average_type2_matrix_nz), int(this->average_type2_invert_nz), - this->ipm_time, this->crossover_time); + this->type1_time, this->basis0_time, this->type2_time, this->ipm_time, this->crossover_time); } else { fprintf(file, "Unknown IPX stats report style of %d\n", int(style)); assert(123 == 456); @@ -1218,6 +1221,9 @@ void HighsIpxStats::initialise() { average_type2_cr_count = 0; average_type2_matrix_nz = 0; average_type2_invert_nz = 0; + type1_time = 0; + basis0_time = 0; + type2_time = 0; ipm_time = 0; crossover_time = 0; } diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index 432c43d41b..c839c6b1cd 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -411,18 +411,24 @@ void LpSolver::RunIPM() { y_start_, zl_start_, zu_start_); } else { + ipx_stats_.type1_time = -control_.Elapsed(); ComputeStartingPoint(ipm); + ipx_stats_.type1_time += control_.Elapsed(); if (info_.status_ipm != IPX_STATUS_not_run) return; RunInitialIPM(ipm); if (info_.status_ipm != IPX_STATUS_not_run) return; } + ipx_stats_.basis0_time = -control_.Elapsed(); BuildStartingBasis(); + ipx_stats_.basis0_time += control_.Elapsed(); if (info_.status_ipm != IPX_STATUS_not_run || info_.centring_tried) return; + ipx_stats_.type2_time = -control_.Elapsed(); RunMainIPM(ipm); + ipx_stats_.type2_time += control_.Elapsed(); } void LpSolver::MakeIPMStartingPointValid() { diff --git a/src/lp_data/HighsSolverStats.h b/src/lp_data/HighsSolverStats.h index 85950a48e3..fd68887ef0 100644 --- a/src/lp_data/HighsSolverStats.h +++ b/src/lp_data/HighsSolverStats.h @@ -86,6 +86,9 @@ struct HighsIpxStats { double average_type2_cr_count; double average_type2_matrix_nz; double average_type2_invert_nz; + double type1_time; + double basis0_time; + double type2_time; double ipm_time; double crossover_time; void workTerms(double* terms); From a33f3380d1fb243c826edac1709707a5dc3a8692 Mon Sep 17 00:00:00 2001 From: JAJHall Date: Thu, 12 Dec 2024 12:12:26 +0000 Subject: [PATCH 40/40] Now timing IPX type1 iterations correctly and ensuring IPX time limit is non-negative --- src/Highs.h | 4 +- src/ipm/IpxWrapper.cpp | 71 +++++++++++++++++++++------------- src/ipm/ipx/lp_solver.cc | 8 ++-- src/lp_data/Highs.cpp | 10 ++--- src/lp_data/HighsSolverStats.h | 12 ++++-- src/simplex/HApp.h | 26 +++++++------ src/simplex/HEkk.cpp | 51 ++++++++++++++---------- 7 files changed, 109 insertions(+), 73 deletions(-) diff --git a/src/Highs.h b/src/Highs.h index 8d22e4fb29..a382435d8c 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1238,8 +1238,8 @@ class Highs { const HighsIpxStats& getIpxStats() { return ipx_stats_; } - void reportIpxStats( - FILE* file, const HighsInt style = HighsSolverStatsReportPretty) { + void reportIpxStats(FILE* file, + const HighsInt style = HighsSolverStatsReportPretty) { ipx_stats_.report(file, "Original LP", style); } diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index c4fcbeef4a..44a78ee568 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -59,6 +59,7 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, // then a basis and primal+dual solution are obtained. // // + const double entry_run_time = timer.readRunHighsClock(); // Indicate that there is no valid primal solution, dual solution or basis highs_basis.valid = false; highs_solution.value_valid = false; @@ -119,7 +120,8 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, parameters.analyse_basis_data = kHighsAnalysisLevelNlaData & options.highs_analysis_level; // Determine the run time allowed for IPX - parameters.time_limit = options.time_limit - timer.readRunHighsClock(); + parameters.time_limit = std::max(options.time_limit - entry_run_time, 0.0); + parameters.ipm_maxiter = options.ipm_iteration_limit - highs_info.ipm_iteration_count; parameters.cr1_maxiter = options.cr1_iteration_limit; @@ -1091,11 +1093,20 @@ void reportSolveData(const HighsLogOptions& log_options, } void HighsIpxStats::workTerms(double* terms) { - const double nonbasic_nz = double(this->num_nz + this->num_row - this->average_type2_matrix_nz); - terms[HighsIpxWorkTermCr1IterNumRow] = double(this->num_type1_iteration) * average_type1_cr_count * double(this->num_row); - terms[HighsIpxWorkTermCr1IterNumNz] = double(this->num_type1_iteration) * average_type1_cr_count * double(this->num_nz); - terms[HighsIpxWorkTermCr2IterNumRow] = double(this->num_type2_iteration) * average_type2_cr_count * double(this->num_row); - terms[HighsIpxWorkTermCr2IterNumNz] = double(this->num_type2_iteration) * average_type2_cr_count * (nonbasic_nz + average_type2_invert_nz); + const double nonbasic_nz = + double(this->num_nz + this->num_row - this->average_type2_matrix_nz); + terms[HighsIpxWorkTermCr1IterNumRow] = double(this->num_type1_iteration) * + average_type1_cr_count * + double(this->num_row); + terms[HighsIpxWorkTermCr1IterNumNz] = double(this->num_type1_iteration) * + average_type1_cr_count * + double(this->num_nz); + terms[HighsIpxWorkTermCr2IterNumRow] = double(this->num_type2_iteration) * + average_type2_cr_count * + double(this->num_row); + terms[HighsIpxWorkTermCr2IterNumNz] = double(this->num_type2_iteration) * + average_type2_cr_count * + (nonbasic_nz + average_type2_invert_nz); } double HighsIpxStats::workEstimate() { @@ -1103,11 +1114,11 @@ double HighsIpxStats::workEstimate() { this->workTerms(terms); double work = 0; for (HighsInt iX = 0; iX < HighsIpxWorkTermCount; iX++) { - assert(terms[iX]>0); + assert(terms[iX] > 0); work += terms[iX] * kIpxWorkCoefficients[iX]; } delete[] terms; - return work; + return work; } void HighsIpxStats::averages() { @@ -1117,8 +1128,7 @@ void HighsIpxStats::averages() { average_type2_cr_count = 0; average_type2_matrix_nz = 0; average_type2_invert_nz = 0; - for (HighsInt iteration = 0; iteration < this->iteration_count; - iteration++) { + for (HighsInt iteration = 0; iteration < this->iteration_count; iteration++) { if (this->cr_type[iteration] == 1) { num_type1_iteration++; average_type1_cr_count += this->cr_count[iteration]; @@ -1166,21 +1176,27 @@ void HighsIpxStats::report(FILE* file, const std::string message, assert(this->iteration_count == HighsInt(this->invert_num_el.size())); assert(this->iteration_count == HighsInt(this->factored_basis_num_el.size())); - if (iteration_count>0) + if (iteration_count > 0) fprintf(file, " Iter type cr_count basisNz invertNz\n"); // printf(file, " dddd d ddddd ddddddd ddddddd\n"); - for (HighsInt iteration = 0; iteration < iteration_count; iteration++) + for (HighsInt iteration = 0; iteration < iteration_count; iteration++) fprintf(file, " %4d %1d %5d %7d %7d\n", int(iteration), int(this->cr_type[iteration]), int(this->cr_count[iteration]), int(this->factored_basis_num_el[iteration]), int(this->invert_num_el[iteration])); fprintf(file, " num_nz = %d\n", this->num_nz); - fprintf(file, " Type 1 iteration = %d\n", this->num_type1_iteration); - fprintf(file, " mean CR count = %g\n", this->average_type1_cr_count); - fprintf(file, " Type 2 iteration = %d\n", this->num_type2_iteration); - fprintf(file, " mean CR count = %g\n", this->average_type2_cr_count); - fprintf(file, " mean CR matrix nz = %g\n", this->average_type2_matrix_nz); - fprintf(file, " mean CR INVERT nz = %g\n", this->average_type2_invert_nz); + fprintf(file, " Type 1 iteration = %d\n", + this->num_type1_iteration); + fprintf(file, " mean CR count = %g\n", + this->average_type1_cr_count); + fprintf(file, " Type 2 iteration = %d\n", + this->num_type2_iteration); + fprintf(file, " mean CR count = %g\n", + this->average_type2_cr_count); + fprintf(file, " mean CR matrix nz = %g\n", + this->average_type2_matrix_nz); + fprintf(file, " mean CR INVERT nz = %g\n", + this->average_type2_invert_nz); fprintf(file, " Type 1 time = %g\n", this->type1_time); fprintf(file, " Starting basis time = %g\n", this->basis0_time); fprintf(file, " Type 2 time = %g\n", this->type2_time); @@ -1189,15 +1205,18 @@ void HighsIpxStats::report(FILE* file, const std::string message, } else if (style == HighsSolverStatsReportCsvHeader) { fprintf(file, "valid,col,row,nz,iteration_count,cr_count,iteration_count, " - "cr_count,matrix_nz,invert_nz,type1_time,basis0_time,type2_time,ipm_time,crossover_time,"); + "cr_count,matrix_nz,invert_nz,type1_time,basis0_time,type2_time," + "ipm_time,crossover_time,"); } else if (style == HighsSolverStatsReportCsvData) { this->averages(); - fprintf(file, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%g,%g,%g,%g,%g,", int(this->valid), - int(this->num_col), int(this->num_row), int(this->num_nz), - int(this->num_type1_iteration), int(this->average_type1_cr_count), - int(this->num_type2_iteration), int(this->average_type2_cr_count), - int(this->average_type2_matrix_nz), int(this->average_type2_invert_nz), - this->type1_time, this->basis0_time, this->type2_time, this->ipm_time, this->crossover_time); + fprintf( + file, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%g,%g,%g,%g,%g,", int(this->valid), + int(this->num_col), int(this->num_row), int(this->num_nz), + int(this->num_type1_iteration), int(this->average_type1_cr_count), + int(this->num_type2_iteration), int(this->average_type2_cr_count), + int(this->average_type2_matrix_nz), int(this->average_type2_invert_nz), + this->type1_time, this->basis0_time, this->type2_time, this->ipm_time, + this->crossover_time); } else { fprintf(file, "Unknown IPX stats report style of %d\n", int(style)); assert(123 == 456); @@ -1225,5 +1244,5 @@ void HighsIpxStats::initialise() { basis0_time = 0; type2_time = 0; ipm_time = 0; - crossover_time = 0; + crossover_time = 0; } diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index c839c6b1cd..b3ec9b6f3d 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -413,10 +413,12 @@ void LpSolver::RunIPM() { else { ipx_stats_.type1_time = -control_.Elapsed(); ComputeStartingPoint(ipm); - ipx_stats_.type1_time += control_.Elapsed(); - if (info_.status_ipm != IPX_STATUS_not_run) - return; + if (info_.status_ipm != IPX_STATUS_not_run) { + ipx_stats_.type1_time += control_.Elapsed(); + return; + } RunInitialIPM(ipm); + ipx_stats_.type1_time += control_.Elapsed(); if (info_.status_ipm != IPX_STATUS_not_run) return; } diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index 7d5c101aaf..c2c18619af 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -1321,7 +1321,7 @@ HighsStatus Highs::solve() { solveLp(incumbent_lp, "Not presolved: solving the LP", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); - simplex_stats_.simplex_time = this_solve_original_lp_time; + simplex_stats_.simplex_time = this_solve_original_lp_time; presolved_lp_simplex_stats_ = simplex_stats_; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); @@ -1336,7 +1336,7 @@ HighsStatus Highs::solve() { solveLp(incumbent_lp, "Problem not reduced by presolve: solving the LP", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); - simplex_stats_.simplex_time = this_solve_original_lp_time; + simplex_stats_.simplex_time = this_solve_original_lp_time; presolved_lp_simplex_stats_ = simplex_stats_; return_status = interpretCallStatus(options_.log_options, call_status, return_status, "callSolveLp"); @@ -1387,7 +1387,7 @@ HighsStatus Highs::solve() { solveLp(reduced_lp, "Solving the presolved LP", this_solve_presolved_lp_time); presolved_lp_simplex_stats_ = this->ekk_instance_.getSimplexStats(); - presolved_lp_simplex_stats_.simplex_time = this_solve_presolved_lp_time; + presolved_lp_simplex_stats_.simplex_time = this_solve_presolved_lp_time; if (ekk_instance_.status_.initialised_for_solve) { // Record the pivot threshold resulting from solving the presolved LP // with simplex @@ -1453,7 +1453,7 @@ HighsStatus Highs::solve() { "to determine infeasible or unbounded", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); - simplex_stats_.simplex_time = this_solve_original_lp_time; + simplex_stats_.simplex_time = this_solve_original_lp_time; // Recover the options options_ = save_options; if (return_status == HighsStatus::kError) @@ -1592,7 +1592,7 @@ HighsStatus Highs::solve() { "Solving the original LP from the solution after postsolve", this_solve_original_lp_time); simplex_stats_ = this->ekk_instance_.getSimplexStats(); - simplex_stats_.simplex_time = this_solve_original_lp_time; + simplex_stats_.simplex_time = this_solve_original_lp_time; // Determine the iteration count postsolve_iteration_count += info_.simplex_iteration_count; return_status = diff --git a/src/lp_data/HighsSolverStats.h b/src/lp_data/HighsSolverStats.h index fd68887ef0..99b8d9f742 100644 --- a/src/lp_data/HighsSolverStats.h +++ b/src/lp_data/HighsSolverStats.h @@ -35,8 +35,11 @@ enum HighsSimplexWorkTerm { HighsSimplexWorkTermCount }; -const std::vector kSimplexWorkNames = {"InvertNumRow", "InvertNumNz", "ComputePD", "Btran", "Price", "Ftran", "FtranDse"}; -const std::vector kSimplexWorkCoefficients = {1.0,2.0,3.0,4.0,5.0,6.0,7.0}; +const std::vector kSimplexWorkNames = { + "InvertNumRow", "InvertNumNz", "ComputePD", "Btran", + "Price", "Ftran", "FtranDse"}; +const std::vector kSimplexWorkCoefficients = {1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0}; enum HighsIpxWorkTerm { HighsIpxWorkTermCr1IterNumRow = 0, @@ -46,7 +49,8 @@ enum HighsIpxWorkTerm { HighsIpxWorkTermCount }; -const std::vector kIpxWorkNames = {"CR1IterNumRow", "CR1IterNumNz", "CR2IterNumRow", "CR2IterNumNz"}; +const std::vector kIpxWorkNames = { + "CR1IterNumRow", "CR1IterNumNz", "CR2IterNumRow", "CR2IterNumNz"}; const std::vector kIpxWorkCoefficients = {1.0, 2.0, 3.0, 4.0}; struct HighsSimplexStats { @@ -90,7 +94,7 @@ struct HighsIpxStats { double basis0_time; double type2_time; double ipm_time; - double crossover_time; + double crossover_time; void workTerms(double* terms); double workEstimate(); void averages(); diff --git a/src/simplex/HApp.h b/src/simplex/HApp.h index 42ec51a53d..28cd8cf3f5 100644 --- a/src/simplex/HApp.h +++ b/src/simplex/HApp.h @@ -334,18 +334,20 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { if (ekk_instance.proofOfPrimalInfeasibility()) solve_unscaled_lp = false; } if (solve_unscaled_lp) { - if (refine_solution) - printf("GrepSolverStats: Model %s requires refinement due to num/max/sum primal " - "(%" HIGHSINT_FORMAT "/%g/%g) and dual (%" HIGHSINT_FORMAT - "/%g/%g) " - "unscaled infeasibilities\n", - incumbent_lp.model_name_.c_str(), - highs_info.num_primal_infeasibilities, - highs_info.max_primal_infeasibility, - highs_info.sum_primal_infeasibilities, - highs_info.num_dual_infeasibilities, - highs_info.max_dual_infeasibility, - highs_info.sum_dual_infeasibilities); + if (refine_solution) + printf( + "GrepSolverStats: Model %s requires refinement due to num/max/sum " + "primal " + "(%" HIGHSINT_FORMAT "/%g/%g) and dual (%" HIGHSINT_FORMAT + "/%g/%g) " + "unscaled infeasibilities\n", + incumbent_lp.model_name_.c_str(), + highs_info.num_primal_infeasibilities, + highs_info.max_primal_infeasibility, + highs_info.sum_primal_infeasibilities, + highs_info.num_dual_infeasibilities, + highs_info.max_dual_infeasibility, + highs_info.sum_dual_infeasibilities); // Save options/strategies that may be changed HighsInt simplex_strategy = options.simplex_strategy; double dual_simplex_cost_perturbation_multiplier = diff --git a/src/simplex/HEkk.cpp b/src/simplex/HEkk.cpp index f3897fb1c7..5006f2d275 100644 --- a/src/simplex/HEkk.cpp +++ b/src/simplex/HEkk.cpp @@ -4449,8 +4449,7 @@ void HighsSimplexStats::report(FILE* file, std::string message, fprintf(file, " row_ap_density = %g\n", this->row_ap_density); fprintf(file, " row_DSE_density = %g\n", this->row_DSE_density); - fprintf(file, " simplex time = = %g\n", - this->simplex_time); + fprintf(file, " simplex time = = %g\n", this->simplex_time); } else if (style == HighsSolverStatsReportCsvHeader) { fprintf(file, "valid,col,row,nz,iteration_count,num_invert,last_factored_basis_" @@ -4464,7 +4463,7 @@ void HighsSimplexStats::report(FILE* file, std::string message, int(this->last_factored_basis_num_el), int(this->last_invert_num_el), this->col_aq_density, this->row_ep_density, this->row_ap_density, this->row_DSE_density, - this->simplex_time); + this->simplex_time); } else { fprintf(file, "Unknown simplex stats report style of %d\n", int(style)); assert(123 == 456); @@ -4488,25 +4487,35 @@ void HighsSimplexStats::initialise(const HighsInt iteration_count_) { } void HighsSimplexStats::workTerms(double* terms) const { - const double nonbasic_nz = double(this->num_nz + this->num_row - this->last_factored_basis_num_el); - terms[HighsSimplexWorkTermInvertNumRow] = double(this->num_invert) * double(this->num_row); - terms[HighsSimplexWorkTermInvertNumNz] = double(this->num_invert) * this->last_factored_basis_num_el; - terms[HighsSimplexWorkTermComputePD] = double(this->num_invert) * double(this->last_invert_num_el + nonbasic_nz); - terms[HighsSimplexWorkTermBtran] = double(this->iteration_count) * double(this->last_invert_num_el) * this->row_ep_density; - terms[HighsSimplexWorkTermPrice] = double(this->iteration_count) * nonbasic_nz * this->row_ep_density; - terms[HighsSimplexWorkTermFtran] = double(this->iteration_count) * double(this->last_invert_num_el) * this->col_aq_density; - terms[HighsSimplexWorkTermFtranDse] = double(this->iteration_count) * double(this->last_invert_num_el) * this->row_DSE_density; + const double nonbasic_nz = + double(this->num_nz + this->num_row - this->last_factored_basis_num_el); + terms[HighsSimplexWorkTermInvertNumRow] = + double(this->num_invert) * double(this->num_row); + terms[HighsSimplexWorkTermInvertNumNz] = + double(this->num_invert) * this->last_factored_basis_num_el; + terms[HighsSimplexWorkTermComputePD] = + double(this->num_invert) * double(this->last_invert_num_el + nonbasic_nz); + terms[HighsSimplexWorkTermBtran] = double(this->iteration_count) * + double(this->last_invert_num_el) * + this->row_ep_density; + terms[HighsSimplexWorkTermPrice] = + double(this->iteration_count) * nonbasic_nz * this->row_ep_density; + terms[HighsSimplexWorkTermFtran] = double(this->iteration_count) * + double(this->last_invert_num_el) * + this->col_aq_density; + terms[HighsSimplexWorkTermFtranDse] = double(this->iteration_count) * + double(this->last_invert_num_el) * + this->row_DSE_density; } - double HighsSimplexStats::workEstimate() const { - double* terms = new double[HighsSimplexWorkTermCount]; - this->workTerms(terms); - double work = 0; - for (HighsInt iX = 0; iX < HighsSimplexWorkTermCount; iX++) { - assert(terms[iX]>0); - work += terms[iX] * kSimplexWorkCoefficients[iX]; - } - delete[] terms; - return work; + double* terms = new double[HighsSimplexWorkTermCount]; + this->workTerms(terms); + double work = 0; + for (HighsInt iX = 0; iX < HighsSimplexWorkTermCount; iX++) { + assert(terms[iX] > 0); + work += terms[iX] * kSimplexWorkCoefficients[iX]; + } + delete[] terms; + return work; }