-
Notifications
You must be signed in to change notification settings - Fork 236
Recompute dual variables and check dual feasibility after basis repair #1965
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
Changes from all commits
c65b3c3
08e5df6
14e7e31
b645303
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 |
|---|---|---|
|
|
@@ -1957,6 +1957,48 @@ f_t dual_infeasibility(const lp_problem_t<i_t, f_t>& lp, | |
| return sum_infeasible; | ||
| } | ||
|
|
||
| template <typename i_t, typename f_t> | ||
| bool recover_dual_feasibility_after_repair(const lp_problem_t<i_t, f_t>& lp, | ||
| const simplex_solver_settings_t<i_t, f_t>& settings, | ||
| basis_update_mpf_t<i_t, f_t>& ft, | ||
| const std::vector<i_t>& basic_list, | ||
| const std::vector<i_t>& nonbasic_list, | ||
| std::vector<variable_status_t>& vstatus, | ||
| const std::vector<f_t>& objective, | ||
| std::vector<f_t>& cB, | ||
| std::vector<f_t>& y, | ||
| std::vector<f_t>& z, | ||
| f_t& work_estimate) | ||
| { | ||
| for (i_t k = 0; k < lp.num_rows; ++k) { | ||
| cB[k] = objective[basic_list[k]]; | ||
| } | ||
| work_estimate += 3 * lp.num_rows; | ||
| ft.b_transpose_solve(cB, y); | ||
| compute_reduced_costs(objective, lp.A, y, basic_list, nonbasic_list, z, work_estimate); | ||
| work_estimate += lp.num_rows + lp.num_cols; | ||
| if (!all_finite(y) || !all_finite(z)) { return false; } | ||
| const f_t before = | ||
| dual_infeasibility(lp, settings, vstatus, z, settings.tight_tol, settings.dual_tol); | ||
| work_estimate += 3 * lp.num_cols; | ||
| if (before <= settings.dual_tol) { return true; } | ||
| for (i_t j : nonbasic_list) { | ||
| if (!std::isfinite(lp.lower[j]) || !std::isfinite(lp.upper[j]) || lp.lower[j] == lp.upper[j]) { | ||
| continue; | ||
| } | ||
| if (vstatus[j] == variable_status_t::NONBASIC_LOWER && z[j] < -settings.dual_tol) { | ||
| vstatus[j] = variable_status_t::NONBASIC_UPPER; | ||
| } else if (vstatus[j] == variable_status_t::NONBASIC_UPPER && z[j] > settings.dual_tol) { | ||
| vstatus[j] = variable_status_t::NONBASIC_LOWER; | ||
| } | ||
| } | ||
| const f_t after = | ||
| dual_infeasibility(lp, settings, vstatus, z, settings.tight_tol, settings.dual_tol); | ||
| work_estimate += 8 * lp.num_cols; | ||
| // The caller must rebuild x and its infeasibilities after these bound changes. | ||
| return after <= settings.dual_tol; | ||
| } | ||
|
|
||
| template <typename i_t, typename f_t> | ||
| f_t primal_infeasibility_breakdown(const lp_problem_t<i_t, f_t>& lp, | ||
| const simplex_solver_settings_t<i_t, f_t>& settings, | ||
|
|
@@ -2599,9 +2641,17 @@ dual_status_t dual_phase2_with_advanced_basis(i_t phase, | |
| assert(nonbasic_list.size() == n - m); | ||
|
|
||
| f_t refactor_start_work = ft.work_estimate(); | ||
| i_t refactor_status = ft.refactor_basis( | ||
| lp.A, settings, lp.lower, lp.upper, start_time, basic_list, nonbasic_list, vstatus); | ||
| refactor_work = ft.work_estimate() - refactor_start_work; | ||
| i_t deficient_repaired = 0; | ||
| i_t refactor_status = ft.refactor_basis(lp.A, | ||
| settings, | ||
| lp.lower, | ||
| lp.upper, | ||
| start_time, | ||
| basic_list, | ||
| nonbasic_list, | ||
| vstatus, | ||
| deficient_repaired); | ||
| refactor_work = ft.work_estimate() - refactor_start_work; | ||
|
Comment on lines
+2644
to
+2654
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. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '1920,2025p' cpp/src/dual_simplex/phase2.cpp
sed -n '2620,2725p' cpp/src/dual_simplex/phase2.cpp
sed -n '2960,3050p' cpp/src/dual_simplex/phase2.cpp
sed -n '3640,3765p' cpp/src/dual_simplex/phase2.cpp
rg -n 'set_primal_variables_on_bounds|refactor_status|deficient_repaired' cpp/src/dual_simplex/phase2.cppRepository: NVIDIA/cuopt Length of output: 21584 🏁 Script executed: #!/bin/bash
sed -n '2235,2325p' cpp/src/dual_simplex/phase2.cpp
rg -n "refactor_basis|all_finite|enum.*dual_status|NUMERICAL|deficient_repaired" cpp/src/dual_simplex --glob '*.{cpp,hpp,h,cu,cuh,inl}'Repository: NVIDIA/cuopt Length of output: 9062 🏁 Script executed: #!/bin/bash
sed -n '2290,2350p' cpp/src/dual_simplex/phase2.cpp
sed -n '2335,2420p' cpp/src/dual_simplex/basis_updates.cpp
sed -n '20,38p' cpp/src/dual_simplex/phase2.hppRepository: NVIDIA/cuopt Length of output: 5960 🏁 Script executed: #!/bin/bash
sed -n '2400,2475p' cpp/src/dual_simplex/basis_updates.cppRepository: NVIDIA/cuopt Length of output: 2446 Check A failed repaired factorization already returns Add the finite check after the reduced-cost computation: 🐛 Proposed fix phase2::compute_reduced_costs(
objective, lp.A, y, basic_list, nonbasic_list, z, phase2_work_estimate);
+ if (!all_finite(y) || !all_finite(z)) { return dual_status_t::NUMERICAL; }🤖 Prompt for AI Agents
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. I'm confused, if we ever perform basis repair we validate that the new y and z are finite and dual feasible. 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.
This path stores
🐇🔍 You are interacting with an AI system.
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. What do you mean the initial basis refactorization path? You mean if initialize_basis is true? 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 mean the if (initialize_basis) {
...
}The concern applies when that call repairs one or more deficient columns ( The later refactor paths call The initial path should either call You are interacting with an AI system. |
||
| if (refactor_status == CONCURRENT_HALT_RETURN) { return dual_status_t::CONCURRENT_LIMIT; } | ||
| if (refactor_status == TIME_LIMIT_RETURN) { return dual_status_t::TIME_LIMIT; } | ||
| if (refactor_status > 0) { return dual_status_t::NUMERICAL; } | ||
|
|
@@ -2934,8 +2984,16 @@ dual_status_t dual_phase2_with_advanced_basis(i_t phase, | |
| iter, | ||
| ft.num_updates()); | ||
| f_t refactor_start_work = ft.work_estimate(); | ||
| i_t refactor_status = ft.refactor_basis( | ||
| lp.A, settings, lp.lower, lp.upper, start_time, basic_list, nonbasic_list, vstatus); | ||
| i_t deficient_repaired = 0; | ||
| i_t refactor_status = ft.refactor_basis(lp.A, | ||
| settings, | ||
| lp.lower, | ||
| lp.upper, | ||
| start_time, | ||
| basic_list, | ||
| nonbasic_list, | ||
| vstatus, | ||
| deficient_repaired); | ||
| if (refactor_status == CONCURRENT_HALT_RETURN) { return dual_status_t::CONCURRENT_LIMIT; } | ||
| if (refactor_status == TIME_LIMIT_RETURN) { return dual_status_t::TIME_LIMIT; } | ||
| if (refactor_status > 0) { return dual_status_t::NUMERICAL; } | ||
|
|
@@ -2945,6 +3003,20 @@ dual_status_t dual_phase2_with_advanced_basis(i_t phase, | |
| basic_list, nonbasic_list, basic_mark, nonbasic_mark, phase2_work_estimate); | ||
| compute_initial_nonbasic_end(basic_mark, Arow, nonbasic_end); | ||
|
|
||
| if (deficient_repaired > 0 && | ||
| !phase2::recover_dual_feasibility_after_repair(lp, | ||
| settings, | ||
| ft, | ||
| basic_list, | ||
| nonbasic_list, | ||
| vstatus, | ||
| objective, | ||
| c_basic, | ||
| y, | ||
| z, | ||
| phase2_work_estimate)) { | ||
| return dual_status_t::NUMERICAL; | ||
| } | ||
| phase2::compute_primal_solution_from_basis( | ||
| lp, ft, basic_list, nonbasic_list, vstatus, x, xB_workspace, phase2_work_estimate); | ||
|
|
||
|
|
@@ -2961,6 +3033,8 @@ dual_status_t dual_phase2_with_advanced_basis(i_t phase, | |
| solve_work = 0.0; | ||
|
|
||
| if (primal_infeasibility > settings.primal_tol) { | ||
| obj = phase2::compute_perturbed_objective(objective, x); | ||
| phase2_work_estimate += 2 * n; | ||
| settings.log.printf( | ||
| "New infeasibilities found after recompute (primal_inf=%.2e). " | ||
| "Continuing phase 2.\n", | ||
|
|
@@ -3591,8 +3665,17 @@ dual_status_t dual_phase2_with_advanced_basis(i_t phase, | |
| num_refactors++; | ||
| bool should_recompute_x = true; // Needed for numerically difficult problems like cbs-cta | ||
| f_t refactor_start_work = ft.work_estimate(); | ||
| i_t refactor_status = ft.refactor_basis( | ||
| lp.A, settings, lp.lower, lp.upper, start_time, basic_list, nonbasic_list, vstatus); | ||
| i_t deficient_repaired = 0; | ||
| i_t refactor_status = ft.refactor_basis(lp.A, | ||
| settings, | ||
| lp.lower, | ||
| lp.upper, | ||
| start_time, | ||
| basic_list, | ||
| nonbasic_list, | ||
| vstatus, | ||
| deficient_repaired); | ||
| const bool did_basis_repair = deficient_repaired > 0; | ||
| if (refactor_status == CONCURRENT_HALT_RETURN) { return dual_status_t::CONCURRENT_LIMIT; } | ||
| if (refactor_status == TIME_LIMIT_RETURN) { return dual_status_t::TIME_LIMIT; } | ||
| if (refactor_status > 0) { | ||
|
|
@@ -3602,8 +3685,15 @@ dual_status_t dual_phase2_with_advanced_basis(i_t phase, | |
| i_t count = 0; | ||
| i_t deficient_size = 0; | ||
| while (true) { | ||
| deficient_size = ft.refactor_basis( | ||
| lp.A, settings, lp.lower, lp.upper, start_time, basic_list, nonbasic_list, vstatus); | ||
| deficient_size = ft.refactor_basis(lp.A, | ||
| settings, | ||
| lp.lower, | ||
| lp.upper, | ||
| start_time, | ||
| basic_list, | ||
| nonbasic_list, | ||
| vstatus, | ||
| deficient_repaired); | ||
| if (deficient_size == CONCURRENT_HALT_RETURN) { | ||
| return dual_status_t::CONCURRENT_LIMIT; | ||
| } | ||
|
|
@@ -3628,6 +3718,20 @@ dual_status_t dual_phase2_with_advanced_basis(i_t phase, | |
| phase2::reset_basis_mark( | ||
| basic_list, nonbasic_list, basic_mark, nonbasic_mark, phase2_work_estimate); | ||
| compute_initial_nonbasic_end(basic_mark, Arow, nonbasic_end); | ||
| if (did_basis_repair && | ||
| !phase2::recover_dual_feasibility_after_repair(lp, | ||
| settings, | ||
| ft, | ||
| basic_list, | ||
| nonbasic_list, | ||
| vstatus, | ||
| objective, | ||
| c_basic, | ||
| y, | ||
| z, | ||
| phase2_work_estimate)) { | ||
| return dual_status_t::NUMERICAL; | ||
| } | ||
| if (should_recompute_x) { | ||
| std::vector<f_t> unperturbed_x(n); | ||
| phase2_work_estimate += n; | ||
|
|
@@ -3641,6 +3745,8 @@ dual_status_t dual_phase2_with_advanced_basis(i_t phase, | |
| phase2_work_estimate); | ||
| x = unperturbed_x; | ||
| phase2_work_estimate += 2 * n; | ||
| obj = phase2::compute_perturbed_objective(objective, x); | ||
| phase2_work_estimate += 2 * n; | ||
| } | ||
| primal_infeasibility_squared = | ||
| phase2::compute_initial_primal_infeasibilities(lp, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.