-
Notifications
You must be signed in to change notification settings - Fork 221
Include objective gap in QP termination criteria #1733
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e1b24f2
269cbd9
29bce4f
ed622ed
1fedd5a
3936874
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4009,16 +4009,20 @@ lp_status_t barrier_solver_t<i_t, f_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<i_t, f_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 && | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The purpose of |
||
| primal_objective == primal_objective) { | ||
| small_gap) { | ||
|
Comment on lines
+4020
to
+4025
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Compute the objective gap in user units.
When Proposed fix- f_t objective_gap = std::abs(primal_objective - dual_objective);
+ f_t objective_gap = std::abs(user_primal_objective - user_dual_objective);
f_t relative_objective_gap =
- objective_gap /
- (1.0 + std::min(std::abs(user_primal_objective), std::abs(primal_objective)));
+ objective_gap / (1.0 + std::abs(user_primal_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 user_dual_objective_save = compute_user_objective(lp, dual_objective_save);
+ f_t objective_gap_save =
+ std::abs(user_primal_objective_save - user_dual_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)));
+ objective_gap_save / (1.0 + std::abs(user_primal_objective_save));
f_t user_primal_objective = compute_user_objective(lp, primal_objective);
+ f_t user_dual_objective = compute_user_objective(lp, dual_objective);
...
- objective_gap = std::abs(primal_objective - dual_objective);
+ objective_gap = std::abs(user_primal_objective - user_dual_objective);Also applies to: 4049-4050, 4056-4080, 4103-4115, 4259-4263, 4287-4292, 4410-4419, 4489-4490, 4508-4509 🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please do not use the language "solver-scale". Please use user to refer to the original unpresolved problem. Please use "presolved" to describe the problem after presolve has been performed. So you can say "primal_objective" and "dual_objective" are the objective of the presolved problem. But do not say "primal_objective" and "dual_objective" are solver-scale values. Please add this as a learning. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🐇💭 ✏️ Learnings added
You are interacting with an AI system. |
||
| 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,24 +4047,39 @@ lp_status_t barrier_solver_t<i_t, f_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<i_t, f_t> Qx_save(data.Q.n); | ||
| dense_vector_t<i_t, f_t> x_save_host(data.Q.n); | ||
| std::copy(data.x_save.begin(), data.x_save.begin() + data.Q.n, x_save_host.begin()); | ||
| 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 = | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only primal info is used in computing
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would you use that info in the denominator? Take the min over the primal and dual objectives?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can take the max over the absolute values of primal and dual, and then min operation over solver objective and user objective.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why max though? min is much more conservative
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, min is stricter. The point is to include both primal and dual info for computation at this line. |
||
| 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<i_t, f_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<i_t, f_t>::solve(f_t start_time, lp_solution_t<i_t, | |
| matrix_vector_multiply(data.Q, 1.0, data.x, 0.0, Qx); | ||
| quad_objective = 0.5 * data.x.inner_product(Qx); | ||
| } | ||
| f_t primal_objective = data.c.inner_product(data.x) + quad_objective; | ||
| f_t primal_objective = data.c.inner_product(data.x) + quad_objective; | ||
| f_t user_primal_objective = compute_user_objective(lp, primal_objective); | ||
|
|
||
| f_t relative_primal_residual = primal_residual_norm / (1.0 + norm_b); | ||
| f_t relative_dual_residual = dual_residual_norm / (1.0 + norm_c); | ||
| f_t relative_complementarity_residual = | ||
| complementarity_residual_norm / | ||
| (1.0 + std::min(std::abs(compute_user_objective(lp, primal_objective)), | ||
| std::abs(primal_objective))); | ||
| (1.0 + std::min(std::abs(user_primal_objective), std::abs(primal_objective))); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should also use dual info for the denominator here. |
||
|
|
||
| dense_vector_t<i_t, f_t> upper(lp.upper); | ||
| data.gather_upper_bounds(upper, data.restrict_u_); | ||
|
|
@@ -4236,11 +4260,11 @@ lp_status_t barrier_solver_t<i_t, f_t>::solve(f_t start_time, lp_solution_t<i_t, | |
| data.d_restrict_u_.data(), data.restrict_u_.data(), data.restrict_u_.size(), stream_view_); | ||
| f_t dual_objective = | ||
| data.b.inner_product(data.y) - data.restrict_u_.inner_product(data.v) - quad_objective; | ||
| f_t user_dual_objective = compute_user_objective(lp, dual_objective); | ||
|
|
||
| f_t objective_gap_abs = std::abs(primal_objective - dual_objective); | ||
| f_t objective_gap_rel = | ||
| objective_gap_abs / | ||
| std::max(f_t(1), std::min(std::abs(primal_objective), std::abs(dual_objective))); | ||
| f_t objective_gap = std::abs(primal_objective - dual_objective); | ||
| f_t relative_objective_gap = | ||
| objective_gap / (1.0 + std::min(std::abs(user_primal_objective), std::abs(primal_objective))); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also need dual info in the denominator. |
||
|
|
||
| data.w_save = data.w; | ||
| data.x_save = data.x; | ||
|
|
@@ -4257,16 +4281,19 @@ lp_status_t barrier_solver_t<i_t, f_t>::solve(f_t start_time, lp_solution_t<i_t, | |
| float64_t elapsed_time = toc(start_time); | ||
| settings.log.printf("%3d %+.12e %+.12e %.2e %.2e %.2e %.1f\n", | ||
| iter, | ||
| compute_user_objective(lp, primal_objective), | ||
| compute_user_objective(lp, dual_objective), | ||
| user_primal_objective, | ||
| user_dual_objective, | ||
| relative_primal_residual, | ||
| relative_dual_residual, | ||
| relative_complementarity_residual, | ||
| elapsed_time); | ||
|
|
||
| bool converged = primal_residual_norm < settings.barrier_relative_feasibility_tol && | ||
| dual_residual_norm < settings.barrier_relative_optimality_tol && | ||
| complementarity_residual_norm < settings.barrier_relative_complementarity_tol; | ||
| bool small_gap = (!data.has_cones() && data.Q.n == 0) || | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's test if we can do this for all problems. Hopefully, we can.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not yet, we have regressions on LP barrier |
||
| relative_objective_gap < settings.barrier_relaxed_relative_objective_gap_tol; | ||
| bool converged = | ||
| primal_residual_norm < settings.barrier_relative_feasibility_tol && | ||
| dual_residual_norm < settings.barrier_relative_optimality_tol && | ||
| complementarity_residual_norm < settings.barrier_relative_complementarity_tol && small_gap; | ||
|
|
||
| const i_t iteration_limit = settings.iteration_limit; | ||
|
|
||
|
|
@@ -4311,9 +4338,11 @@ lp_status_t barrier_solver_t<i_t, f_t>::solve(f_t start_time, lp_solution_t<i_t, | |
| primal_residual_norm, | ||
| dual_residual_norm, | ||
| complementarity_residual_norm, | ||
| objective_gap, | ||
| relative_primal_residual, | ||
| relative_dual_residual, | ||
| relative_complementarity_residual, | ||
| relative_objective_gap, | ||
| solution); | ||
| } | ||
| if (toc(start_time) > settings.time_limit) { | ||
|
|
@@ -4351,9 +4380,11 @@ lp_status_t barrier_solver_t<i_t, f_t>::solve(f_t start_time, lp_solution_t<i_t, | |
| primal_residual_norm, | ||
| dual_residual_norm, | ||
| complementarity_residual_norm, | ||
| objective_gap, | ||
| relative_primal_residual, | ||
| relative_dual_residual, | ||
| relative_complementarity_residual, | ||
| relative_objective_gap, | ||
| solution); | ||
| } | ||
| data.has_factorization = false; | ||
|
|
@@ -4380,17 +4411,16 @@ lp_status_t barrier_solver_t<i_t, f_t>::solve(f_t start_time, lp_solution_t<i_t, | |
|
|
||
| compute_primal_dual_objective(data, primal_objective, dual_objective); | ||
|
|
||
| relative_primal_residual = primal_residual_norm / (1.0 + norm_b); | ||
| relative_dual_residual = dual_residual_norm / (1.0 + norm_c); | ||
| f_t user_primal_objective = compute_user_objective(lp, primal_objective); | ||
| relative_primal_residual = primal_residual_norm / (1.0 + norm_b); | ||
| relative_dual_residual = dual_residual_norm / (1.0 + norm_c); | ||
| relative_complementarity_residual = | ||
| complementarity_residual_norm / | ||
| (1.0 + std::min(std::abs(compute_user_objective(lp, primal_objective)), | ||
| std::abs(primal_objective))); | ||
| (1.0 + std::min(std::abs(user_primal_objective), std::abs(primal_objective))); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same issue for missing dual info. |
||
|
|
||
| objective_gap_abs = std::abs(primal_objective - dual_objective); | ||
| objective_gap_rel = | ||
| objective_gap_abs / | ||
| std::max(f_t(1), std::min(std::abs(primal_objective), std::abs(dual_objective))); | ||
| objective_gap = std::abs(primal_objective - dual_objective); | ||
| relative_objective_gap = objective_gap / (1.0 + std::min(std::abs(user_primal_objective), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same issue for missing dual info. |
||
| std::abs(primal_objective))); | ||
|
|
||
| if (relative_primal_residual < settings.barrier_relaxed_feasibility_tol && | ||
| relative_dual_residual < settings.barrier_relaxed_optimality_tol && | ||
|
|
@@ -4438,9 +4468,11 @@ lp_status_t barrier_solver_t<i_t, f_t>::solve(f_t start_time, lp_solution_t<i_t, | |
| primal_residual_norm, | ||
| dual_residual_norm, | ||
| complementarity_residual_norm, | ||
| objective_gap, | ||
| relative_primal_residual, | ||
| relative_dual_residual, | ||
| relative_complementarity_residual, | ||
| relative_objective_gap, | ||
| solution); | ||
| } | ||
|
|
||
|
|
@@ -4458,7 +4490,8 @@ lp_status_t barrier_solver_t<i_t, f_t>::solve(f_t start_time, lp_solution_t<i_t, | |
| bool small_gap = | ||
| relative_complementarity_residual < settings.barrier_relative_complementarity_tol; | ||
| bool small_objective_gap = | ||
| !data.has_cones() || objective_gap_rel < settings.barrier_relaxed_complementarity_tol; | ||
| (!data.has_cones() && data.Q.n == 0) || | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same concern for |
||
| relative_objective_gap < settings.barrier_relative_objective_gap_tol; | ||
|
|
||
| converged = primal_feasible && dual_feasible && small_gap && small_objective_gap; | ||
|
|
||
|
|
@@ -4476,6 +4509,8 @@ lp_status_t barrier_solver_t<i_t, f_t>::solve(f_t start_time, lp_solution_t<i_t, | |
| 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); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| settings.log.printf("\n"); | ||
| 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_); | ||
|
|
@@ -4510,9 +4545,11 @@ lp_status_t barrier_solver_t<i_t, f_t>::solve(f_t start_time, lp_solution_t<i_t, | |
| primal_residual_norm, | ||
| dual_residual_norm, | ||
| complementarity_residual_norm, | ||
| objective_gap, | ||
| relative_primal_residual, | ||
| relative_dual_residual, | ||
| relative_complementarity_residual, | ||
| relative_objective_gap, | ||
| solution); | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need
(!data.has_cones() && data.Q.n == 0)? I think we should have duality gap check for all kinds of problems solved by barrier.