From 40e45c9d15cdb74873e649c6488250bca2e71e1d Mon Sep 17 00:00:00 2001 From: JAJHall Date: Tue, 22 Oct 2024 22:00:01 +0100 Subject: [PATCH] Fixed #1865; formatted --- src/lp_data/HighsOptions.h | 2 +- src/lp_data/HighsSolve.cpp | 2 +- src/simplex/HApp.h | 80 +++++++++++++++++++++++++++----------- src/simplex/HEkkDual.cpp | 8 ++-- src/simplex/HEkkPrimal.cpp | 8 +++- src/util/HFactor.h | 2 +- 6 files changed, 70 insertions(+), 32 deletions(-) diff --git a/src/lp_data/HighsOptions.h b/src/lp_data/HighsOptions.h index bf853a4839..9b38657551 100644 --- a/src/lp_data/HighsOptions.h +++ b/src/lp_data/HighsOptions.h @@ -559,7 +559,7 @@ struct HighsOptionsStruct { #endif mip_improving_solution_save(false), mip_improving_solution_report_sparse(false), - mip_improving_solution_file("") {}; + mip_improving_solution_file(""){}; }; // For now, but later change so HiGHS properties are string based so that new diff --git a/src/lp_data/HighsSolve.cpp b/src/lp_data/HighsSolve.cpp index de490e914b..988e435e25 100644 --- a/src/lp_data/HighsSolve.cpp +++ b/src/lp_data/HighsSolve.cpp @@ -139,7 +139,7 @@ HighsStatus solveLp(HighsLpSolverObject& solver_object, const string message) { return HighsStatus::kError; } } // options.run_crossover == kHighsOnString - } // unwelcome_ipx_status + } // unwelcome_ipx_status } else { // PDLP has been used, so check whether claim of optimality // satisfies the HiGHS criteria diff --git a/src/simplex/HApp.h b/src/simplex/HApp.h index 9e1be51f85..444e34e28c 100644 --- a/src/simplex/HApp.h +++ b/src/simplex/HApp.h @@ -250,12 +250,27 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { scaled_model_status == HighsModelStatus::kOptimal && (num_unscaled_primal_infeasibilities || num_unscaled_dual_infeasibilities); - if (scaled_optimality_but_unscaled_infeasibilities) + // Determine whether the unscaled solution has primal + // infeasibilities after the scaled LP has been solved to the + // objective target + const bool scaled_objective_target_but_unscaled_primal_infeasibilities = + scaled_model_status == HighsModelStatus::kObjectiveTarget && + highs_info.num_primal_infeasibilities > 0; + // Determine whether the unscaled solution has dual + // infeasibilities after the scaled LP has been solved to the + // objective bound + const bool scaled_objective_bound_but_unscaled_dual_infeasibilities = + scaled_model_status == HighsModelStatus::kObjectiveBound && + highs_info.num_dual_infeasibilities > 0; + if (scaled_optimality_but_unscaled_infeasibilities || + scaled_objective_target_but_unscaled_primal_infeasibilities || + scaled_objective_bound_but_unscaled_dual_infeasibilities) highsLogDev(options.log_options, HighsLogType::kInfo, - "Have num/max/sum primal (%" HIGHSINT_FORMAT - "/%g/%g) and dual (%" HIGHSINT_FORMAT + "After unscaling with status %s, have num/max/sum primal " + "(%" HIGHSINT_FORMAT "/%g/%g) and dual (%" HIGHSINT_FORMAT "/%g/%g) " "unscaled infeasibilities\n", + utilModelStatusToString(scaled_model_status).c_str(), highs_info.num_primal_infeasibilities, highs_info.max_primal_infeasibility, highs_info.sum_primal_infeasibilities, @@ -270,8 +285,8 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { scaled_model_status == HighsModelStatus::kInfeasible || scaled_model_status == HighsModelStatus::kUnboundedOrInfeasible || scaled_model_status == HighsModelStatus::kUnbounded || - scaled_model_status == HighsModelStatus::kObjectiveBound || - scaled_model_status == HighsModelStatus::kObjectiveTarget || + scaled_objective_bound_but_unscaled_dual_infeasibilities || + scaled_objective_target_but_unscaled_primal_infeasibilities || scaled_model_status == HighsModelStatus::kUnknown); // Handle the case when refinement will not take place if (!refine_solution) { @@ -318,21 +333,31 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { 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::kObjectiveBound) { - // Only dual infeasibilities, or primal infeasibilities do not - // matter due to solution status, so use primal simplex phase - // 2 + scaled_model_status == HighsModelStatus::kObjectiveTarget) { + // Only dual infeasibilities, or objective target reached (in + // primal phase 2) - so primal infeasibilities should be small + // - so use primal simplex phase 2 options.simplex_strategy = kSimplexStrategyPrimal; - if (scaled_model_status == HighsModelStatus::kObjectiveBound) { - highsLogDev( - options.log_options, HighsLogType::kInfo, - "solveLpSimplex: Calling primal simplex after " - "scaled_model_status == HighsModelStatus::kObjectiveBound: solve " - "= %d; tick = %d; iter = %d\n", - (int)ekk_instance.debug_solve_call_num_, - (int)ekk_instance.debug_initial_build_synthetic_tick_, - (int)ekk_instance.iteration_count_); + if (scaled_model_status == HighsModelStatus::kObjectiveTarget) { + highsLogDev(options.log_options, HighsLogType::kInfo, + "solveLpSimplex: Calling primal simplex after " + "scaled_model_status == " + "HighsModelStatus::kObjectiveTarget: solve " + "= %d; tick = %d; iter = %d\n", + (int)ekk_instance.debug_solve_call_num_, + (int)ekk_instance.debug_initial_build_synthetic_tick_, + (int)ekk_instance.iteration_count_); } } else { // Using dual simplex, so force Devex if starting from an advanced @@ -345,11 +370,21 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { // // Solve the unscaled LP with scaled NLA // - // Force the simplex solver to start in phase 2 unless solving - // the LP directly as unscaled + // Force the simplex solver to start in phase 1 if solving the + // LP directly as unscaled, or using primal simplex to clean up + // small dual infeasibilities after the scaled LP yielded model + // status HighsModelStatus::kObjectiveTarget. Otherwise force + // the simplex solver to start in phase 2 // - const bool force_phase2 = options.simplex_unscaled_solution_strategy != - kSimplexUnscaledSolutionStrategyDirect; + const bool force_phase1 = + (options.simplex_unscaled_solution_strategy == + kSimplexUnscaledSolutionStrategyDirect) || + (scaled_model_status == HighsModelStatus::kObjectiveTarget); + const bool force_phase2 = + (options.simplex_unscaled_solution_strategy != + kSimplexUnscaledSolutionStrategyDirect) && + (scaled_model_status != HighsModelStatus::kObjectiveTarget); + assert(force_phase2 == !force_phase1); return_status = ekk_instance.solve(force_phase2); solved_unscaled_lp = true; if (scaled_model_status != HighsModelStatus::kObjectiveBound && @@ -358,7 +393,6 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) { // for the first time in which case we again call solve with primal // simplex if not dual feasible const bool objective_bound_refinement = - ekk_instance.model_status_ == HighsModelStatus::kObjectiveBound && ekk_info.num_dual_infeasibilities > 0; if (objective_bound_refinement) { options.simplex_strategy = kSimplexStrategyPrimal; diff --git a/src/simplex/HEkkDual.cpp b/src/simplex/HEkkDual.cpp index a9f53ce0ee..e05633b376 100644 --- a/src/simplex/HEkkDual.cpp +++ b/src/simplex/HEkkDual.cpp @@ -2807,10 +2807,10 @@ bool HEkkDual::reachedExactObjectiveBound() { exact_dual_objective_value - objective_bound; std::string action; if (exact_dual_objective_value > objective_bound) { - highsLogDev(ekk_instance_.options_->log_options, HighsLogType::kDetailed, - "HEkkDual::solvePhase2: %12g = Objective > ObjectiveUB\n", - ekk_instance_.info_.updated_dual_objective_value, - objective_bound); + highsLogDev( + ekk_instance_.options_->log_options, HighsLogType::kDetailed, + "HEkkDual::solvePhase2: %12g = Objective > ObjectiveUB = %12g\n", + ekk_instance_.info_.updated_dual_objective_value, objective_bound); action = "Have DualUB bailout"; if (ekk_instance_.info_.costs_perturbed || ekk_instance_.info_.costs_shifted) { diff --git a/src/simplex/HEkkPrimal.cpp b/src/simplex/HEkkPrimal.cpp index 80510f6d0d..5d6509c972 100644 --- a/src/simplex/HEkkPrimal.cpp +++ b/src/simplex/HEkkPrimal.cpp @@ -2813,8 +2813,12 @@ void HEkkPrimal::shiftBound(const bool lower, const HighsInt iVar, bound -= shift; new_infeasibility = bound - value; if (new_infeasibility >= 0) { - printf("HEkkPrimal::shiftBound LB = %g; random_value = %g; value = %g; feasibility = %g; infeasibility = %g; shift = %g; bound = %g; new_infeasibility = %g; \n", - old_bound, random_value, value, feasibility, infeasibility, shift, bound, new_infeasibility); + printf( + "HEkkPrimal::shiftBound LB = %g; random_value = %g; value = %g; " + "feasibility = %g; infeasibility = %g; shift = %g; bound = %g; " + "new_infeasibility = %g; \n", + old_bound, random_value, value, feasibility, infeasibility, shift, + bound, new_infeasibility); fflush(stdout); } assert(new_infeasibility < 0); diff --git a/src/util/HFactor.h b/src/util/HFactor.h index 03cb8d8b1d..2f75260d8a 100644 --- a/src/util/HFactor.h +++ b/src/util/HFactor.h @@ -132,7 +132,7 @@ class HFactor { build_timer_(nullptr), nwork(0), u_merit_x(0), - u_total_x(0) {}; + u_total_x(0){}; /** * @brief Copy problem size and pointers of constraint matrix, and set