Fix the bug detecting nonconvex quadratic constraints in the general …#1438
Fix the bug detecting nonconvex quadratic constraints in the general …#1438yuwenchen95 wants to merge 4 commits into
Conversation
…path Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
cedb27f to
07f7b3f
Compare
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe PR updates cross-only indefinite quadratic handling in LDLT-based SOC conversion, adds runtime rejection for degenerate factorization results, routes MPS problem population through a helper, and extends tests for quadratic-constraint preservation and solver rejection. ChangesCross-only indefinite QCQP handling
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cpp/src/dual_simplex/right_looking_lu.cpp`:
- Around line 1686-1687: Add a new method `has_remaining_numerically_nonzero` to
the `symmetric_trailing_matrix_t` class that checks whether the trailing matrix
has any numerically nonzero elements by iterating through diagonal elements and
off-diagonal elements, comparing them against a numeric tolerance parameter.
Then, refine the indefinite return condition around line 1726 (in the 1723-1727
range) so that it returns indefinite not only when `pivots == 0`, but also when
the trailing matrix is still numerically nonzero after partial elimination,
ensuring mixed cases with one early pivot followed by remaining cross-only
indefinite blocks are properly handled.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 8a87302e-97ad-4c39-a947-ab3d6cfcd39e
📒 Files selected for processing (5)
cpp/src/barrier/translate_soc.hppcpp/src/dual_simplex/right_looking_lu.cppcpp/src/dual_simplex/right_looking_lu.hppcpp/tests/dual_simplex/unit_tests/right_looking_ldlt.cppcpp/tests/socp/general_quadratic_test.cu
| const i_t n = A.n; | ||
| const i_t input_nnz = A.nnz(); |
There was a problem hiding this comment.
Handle no-pivot stalls after partial elimination as indefinite when the trailing matrix is still nonzero.
At Line 1726, the indefinite return is gated by pivots == 0, so mixed cases can still slip through (e.g., one valid early pivot, then a remaining cross-only indefinite block). That returns a positive rank and can still let non-convex QCQP pass conversion.
🛠️ Suggested fix
- const i_t n = A.n;
- const i_t input_nnz = A.nnz();
+ const i_t n = A.n;
...
- if (pivot_p == -1) {
- // No acceptable diagonal pivot. Zero matrix is rank 0; nonzero cross-only indefinite
- // matrices (e.g. [[0, a]; [a, 0]]) also stall here and must not be treated as PSD.
- if (pivots == 0 && input_nnz > 0) { return INDEFINITE_MATRIX_RETURN; }
- break;
- }
+ if (pivot_p == -1) {
+ // If any significant entry remains in the trailing matrix, matrix is not PSD.
+ if (trailing_matrix.has_remaining_numerically_nonzero(pivot_tol)) {
+ return INDEFINITE_MATRIX_RETURN;
+ }
+ break; // true rank-deficient zero remainder
+ }// Add to symmetric_trailing_matrix_t
bool has_remaining_numerically_nonzero(f_t nz_tol) const
{
for (i_t j = 0; j < n_; ++j) {
if (std::abs(diag_[j]) >= nz_tol) { return true; }
for (i_t p = col_start_[j]; p < col_end_[j]; ++p) {
if (std::abs(c_x_[p]) >= nz_tol) { return true; }
}
}
return false;
}As per coding guidelines, solver reviews should catch degenerate/numerical transformation-path correctness gaps.
Also applies to: 1723-1727
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cpp/src/dual_simplex/right_looking_lu.cpp` around lines 1686 - 1687, Add a
new method `has_remaining_numerically_nonzero` to the
`symmetric_trailing_matrix_t` class that checks whether the trailing matrix has
any numerically nonzero elements by iterating through diagonal elements and
off-diagonal elements, comparing them against a numeric tolerance parameter.
Then, refine the indefinite return condition around line 1726 (in the 1723-1727
range) so that it returns indefinite not only when `pivots == 0`, but also when
the trailing matrix is still numerically nonzero after partial elimination,
ensuring mixed cases with one early pivot followed by remaining cross-only
indefinite blocks are properly handled.
Source: Coding guidelines
Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
7ef012d to
6e82619
Compare
| rotated_cones.reserve(qcs.size()); | ||
| std::vector<f_t> qc_soc_uniform_scale(qcs.size(), 1); | ||
|
|
||
| for (size_t qc_i = 0; qc_i < qcs.size(); ++qc_i) { |
There was a problem hiding this comment.
How long does the conversion take? I think this loop can be parallelized to speed this up.
You can do it as another PR though.
There was a problem hiding this comment.
It really depends on the problem class. Usually it takes less than 1%, but there is a problem class that may take up to 8% of total solve time.
I would do it later in another PR aiming efficiency improvement of setup phase for QCQPs.
4f4bb20 to
561e423
Compare
Delegate mps_data_model_to_optimization_problem to populate_from_mps_data_model so quadratic constraints are not dropped before solve_lp. Convert QCs in the optimization_problem_interface cuopt_problem_to_user_problem overload. Replace the internal SOC converter test for issue NVIDIA#1434 with an end-to-end solve_lp test and add a regression test for QC round-trip through mps_data_model_to_optimization_problem. Signed-off-by: YUWEN Chen <yuwchen@computelab-build-5.nvidia.com> Signed-off-by: YUWEN Chen <yuwchen@nvidia.com> Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
561e423 to
5b2682d
Compare
Signed-off-by: yuwenchen95 <yuwchen@nvidia.com>
|
/ok to test e863795 |
Description
Cross-only indefinite quadratic constraints (e.g.
2·x₀·x₁ ≤ 0.5withH = [[0,2],[2,0]]) previously passed convexity checks on the general SOC conversion path. Diagonal LDLT returnedrank = 0withoutINDEFINITE_MATRIX_RETURN, and a degenerater = 0SOC lift silently dropped the quadratic term, allowing wrong solutions to be reported as Optimal (e.g. obj=2 at (1,1) instead of rejecting the problem).Fixes:
right_looking_ldlt: returnINDEFINITE_MATRIX_RETURNwhen factorization stalls atrank = 0on a nonzero matrix.translate_soc.hpp(general path):cuopt_expects(rank >= 1, …)before building the SOC lift.Adds LDLT and SOC-conversion unit tests for the cross-only indefinite case.
Issue
closes #1434
Checklist
Test plan
./build.sh libcuoptwith conda env active, then run built/installedcuopt_cliagainst$CONDA_PREFIX/lib/libcuopt.so./cpp/build/tests/dual_simplex/DUAL_SIMPLEX_TEST --gtest_filter='right_looking_ldlt.indefinite_cross_only_2x2'./cpp/build/tests/socp/SOCP_TEST --gtest_filter='general_quadratic.rejects_cross_only_indefinite'./cpp/build/tests/socp/SOCP_TEST --gtest_filter='general_quadratic.rejects_non_convex'datasets/qcqp/issue_1434_nonconvex.lp→ValidationError(not Optimal obj=2)