diff --git a/cpp/src/barrier/barrier.cu b/cpp/src/barrier/barrier.cu index c164296a25..cc0c00c36b 100644 --- a/cpp/src/barrier/barrier.cu +++ b/cpp/src/barrier/barrier.cu @@ -4009,16 +4009,20 @@ lp_status_t barrier_solver_t::check_for_suboptimal_solution( f_t& primal_residual_norm, f_t& dual_residual_norm, f_t& complementarity_residual_norm, + f_t& objective_gap, f_t& relative_primal_residual, f_t& relative_dual_residual, f_t& relative_complementarity_residual, + f_t& relative_objective_gap, lp_solution_t& solution) { raft::common::nvtx::range fun_scope("Barrier: check_for_suboptimal_solution"); + bool small_gap = (!data.has_cones() && data.Q.n == 0) || + relative_objective_gap < settings.barrier_relaxed_relative_objective_gap_tol; if (relative_primal_residual < settings.barrier_relaxed_feasibility_tol && relative_dual_residual < settings.barrier_relaxed_optimality_tol && relative_complementarity_residual < settings.barrier_relaxed_complementarity_tol && - primal_objective == primal_objective) { + small_gap) { raft::copy(data.x.data(), data.d_x_.data(), data.d_x_.size(), stream_view_); raft::copy(data.y.data(), data.d_y_.data(), data.d_y_.size(), stream_view_); raft::copy(data.z.data(), data.d_z_.data(), data.d_z_.size(), stream_view_); @@ -4043,12 +4047,16 @@ lp_status_t barrier_solver_t::check_for_suboptimal_solution( settings.log.printf("Complementarity gap (abs/rel): %8.2e/%8.2e\n", complementarity_residual_norm, relative_complementarity_residual); + settings.log.printf( + "Objective gap (abs/rel): %8.2e/%8.2e\n", objective_gap, relative_objective_gap); settings.log.printf("\n"); return lp_status_t::OPTIMAL; // TODO: Barrier should probably have a separate suboptimal // status } f_t primal_objective_save = data.c.inner_product(data.x_save); + f_t dual_objective_save = + data.b.inner_product(data.y_save) - data.restrict_u_.inner_product(data.v_save); if (data.Q.n > 0) { dense_vector_t Qx_save(data.Q.n); dense_vector_t x_save_host(data.Q.n); @@ -4056,11 +4064,22 @@ lp_status_t barrier_solver_t::check_for_suboptimal_solution( matrix_vector_multiply(data.Q, 1.0, x_save_host, 0.0, Qx_save); f_t quad_objective = 0.5 * x_save_host.inner_product(Qx_save); primal_objective_save += quad_objective; + dual_objective_save -= quad_objective; } + f_t objective_gap_save = std::abs(primal_objective_save - dual_objective_save); + f_t user_primal_objective_save = compute_user_objective(lp, primal_objective_save); + f_t relative_objective_gap_save = + objective_gap_save / + (1.0 + std::min(std::abs(user_primal_objective_save), std::abs(primal_objective_save))); + bool small_gap_save = + (!data.has_cones() && data.Q.n == 0) || + relative_objective_gap_save < settings.barrier_relaxed_relative_objective_gap_tol; + if (data.relative_primal_residual_save < settings.barrier_relaxed_feasibility_tol && data.relative_dual_residual_save < settings.barrier_relaxed_optimality_tol && - data.relative_complementarity_residual_save < settings.barrier_relaxed_complementarity_tol) { + data.relative_complementarity_residual_save < settings.barrier_relaxed_complementarity_tol && + small_gap_save) { settings.log.printf("Restoring previous solution\n"); data.restore_saved_iterate(); data.to_solution(lp, @@ -4083,14 +4102,19 @@ lp_status_t barrier_solver_t::check_for_suboptimal_solution( settings.log.printf("Complementarity gap (abs/rel): %8.2e/%8.2e\n", data.complementarity_residual_norm_save, data.relative_complementarity_residual_save); + settings.log.printf("Objective gap (abs/rel): %8.2e/%8.2e\n", + objective_gap_save, + relative_objective_gap_save); settings.log.printf("\n"); return lp_status_t::OPTIMAL; // TODO: Barrier should probably have a separate suboptimal // status } else { - settings.log.printf("Primal residual %.2e dual residual %.2e complementarity residual %.2e\n", - relative_primal_residual, - relative_dual_residual, - relative_complementarity_residual); + settings.log.printf( + "Primal residual %.2e dual residual %.2e complementarity residual %.2e objective gap %.2e\n", + relative_primal_residual, + relative_dual_residual, + relative_complementarity_residual, + relative_objective_gap); } settings.log.printf("Search direction computation failed\n"); return lp_status_t::NUMERICAL_ISSUES; @@ -4220,14 +4244,14 @@ lp_status_t barrier_solver_t::solve(f_t start_time, lp_solution_t upper(lp.upper); data.gather_upper_bounds(upper, data.restrict_u_); @@ -4236,11 +4260,11 @@ lp_status_t barrier_solver_t::solve(f_t start_time, lp_solution_t::solve(f_t start_time, lp_solution_t::solve(f_t start_time, lp_solution_t settings.time_limit) { @@ -4351,9 +4380,11 @@ lp_status_t barrier_solver_t::solve(f_t start_time, lp_solution_t::solve(f_t start_time, lp_solution_t::solve(f_t start_time, lp_solution_t::solve(f_t start_time, lp_solution_t::solve(f_t start_time, lp_solution_t::solve(f_t start_time, lp_solution_t& solution); const simplex::lp_problem_t& lp; diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index c7fe06ed4a..febd7298e7 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -48,9 +48,11 @@ struct simplex_solver_settings_t { barrier_relative_feasibility_tol(1e-8), barrier_relative_optimality_tol(1e-8), barrier_relative_complementarity_tol(1e-8), + barrier_relative_objective_gap_tol(1e-6), barrier_relaxed_feasibility_tol(1e-4), barrier_relaxed_optimality_tol(1e-4), barrier_relaxed_complementarity_tol(1e-4), + barrier_relaxed_relative_objective_gap_tol(1e-4), cut_off(std::numeric_limits::infinity()), steepest_edge_ratio(0.5), steepest_edge_primal_tol(1e-9), @@ -140,9 +142,12 @@ struct simplex_solver_settings_t { f_t barrier_relative_optimality_tol; // Relative optimality tolerance for barrier method f_t barrier_relative_complementarity_tol; // Relative complementarity tolerance for barrier method + f_t barrier_relative_objective_gap_tol; // Relative objective gap tolerance for barrier method f_t barrier_relaxed_feasibility_tol; // Relative feasibility tolerance for barrier method f_t barrier_relaxed_optimality_tol; // Relative optimality tolerance for barrier method f_t barrier_relaxed_complementarity_tol; // Relative complementarity tolerance for barrier method + f_t barrier_relaxed_relative_objective_gap_tol; // Relative objective gap tolerance for barrier + // method f_t cut_off; // If the dual objective is greater than the cutoff we stop f_t steepest_edge_ratio; // the ratio of computed steepest edge mismatch from updated steepest edge diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index 80b3da2c18..2ef1ea7874 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -522,6 +522,8 @@ std::tuple, simplex::lp_status_t, f_t, f_t, f_t barrier_settings.barrier_relaxed_feasibility_tol = settings.tolerances.relative_primal_tolerance; barrier_settings.barrier_relaxed_optimality_tol = settings.tolerances.relative_dual_tolerance; barrier_settings.barrier_relaxed_complementarity_tol = settings.tolerances.relative_gap_tolerance; + barrier_settings.barrier_relaxed_relative_objective_gap_tol = + settings.tolerances.relative_gap_tolerance; if (barrier_settings.concurrent_halt != nullptr) { // Don't show the barrier log in concurrent mode. Show the PDLP log instead barrier_settings.log.log = false;