|
5 | 5 | */ |
6 | 6 | /* clang-format on */ |
7 | 7 |
|
| 8 | +#include <gmock/gmock.h> |
8 | 9 | #include <gtest/gtest.h> |
9 | 10 |
|
10 | 11 | #include <barrier/translate_soc.hpp> |
| 12 | +#include <cuopt/error.hpp> |
| 13 | +#include <cuopt/mathematical_optimization/io/parser.hpp> |
11 | 14 | #include <cuopt/mathematical_optimization/optimization_problem_interface.hpp> |
12 | 15 | #include <dual_simplex/solve.hpp> |
13 | 16 | #include <dual_simplex/sparse_matrix.hpp> |
@@ -214,6 +217,90 @@ TEST(general_quadratic, rejects_non_convex) |
214 | 217 | cuopt::logic_error); |
215 | 218 | } |
216 | 219 |
|
| 220 | +// End-to-end: cross-only indefinite Q (issue #1434). H = [[0, 2]; [2, 0]] has zero diagonals so |
| 221 | +// LDLT returns rank 0 without a negative pivot; must still be rejected as non-convex via |
| 222 | +// solve_qcqp. |
| 223 | +TEST(general_quadratic, rejects_cross_only_indefinite) |
| 224 | +{ |
| 225 | + raft::handle_t handle{}; |
| 226 | + init_handler(&handle); |
| 227 | + |
| 228 | + // H = [[0, 2]; [2, 0]] from [ 4 x * y ] in LP bracket notation. |
| 229 | + auto lp = io::read_lp_from_string<i_t, f_t>(R"LP( |
| 230 | +Minimize |
| 231 | + obj: x + y |
| 232 | +Subject To |
| 233 | + q0: [ 4 x * y ] <= 0.5 |
| 234 | +Bounds |
| 235 | + -1 <= x <= 1 |
| 236 | + -1 <= y <= 1 |
| 237 | +End |
| 238 | +)LP"); |
| 239 | + |
| 240 | + ASSERT_TRUE(lp.has_quadratic_constraints()); |
| 241 | + ASSERT_EQ(lp.get_quadratic_constraints().size(), 1u); |
| 242 | + |
| 243 | + const i_t n = lp.get_n_variables(); |
| 244 | + const i_t m = lp.get_n_constraints(); |
| 245 | + EXPECT_EQ(n, 2); |
| 246 | + EXPECT_EQ(m, 0); |
| 247 | + |
| 248 | + user_problem_t<i_t, f_t> user_problem(&handle); |
| 249 | + user_problem.num_rows = m; |
| 250 | + user_problem.num_cols = n; |
| 251 | + user_problem.objective = lp.get_objective_coefficients(); |
| 252 | + |
| 253 | + // Initialize the empty A matrix |
| 254 | + user_problem.A.m = m; |
| 255 | + user_problem.A.n = n; |
| 256 | + user_problem.A.nz_max = 0; |
| 257 | + user_problem.A.reallocate(0); |
| 258 | + user_problem.A.col_start.assign(n + 1, 0); |
| 259 | + |
| 260 | + user_problem.rhs.clear(); |
| 261 | + user_problem.row_sense.clear(); |
| 262 | + user_problem.lower = lp.get_variable_lower_bounds(); |
| 263 | + user_problem.upper = lp.get_variable_upper_bounds(); |
| 264 | + user_problem.num_range_rows = 0; |
| 265 | + user_problem.var_types.assign(n, variable_type_t::CONTINUOUS); |
| 266 | + |
| 267 | + const auto& src_qc = lp.get_quadratic_constraints()[0]; |
| 268 | + qc_t qc; |
| 269 | + qc.constraint_row_index = src_qc.constraint_row_index; |
| 270 | + qc.constraint_row_name = src_qc.constraint_row_name; |
| 271 | + qc.constraint_row_type = src_qc.constraint_row_type; |
| 272 | + qc.linear_values = src_qc.linear_values; |
| 273 | + qc.linear_indices = src_qc.linear_indices; |
| 274 | + qc.rhs_value = src_qc.rhs_value; |
| 275 | + qc.rows = src_qc.rows; |
| 276 | + qc.cols = src_qc.cols; |
| 277 | + qc.vals = src_qc.vals; |
| 278 | + |
| 279 | + csr_matrix_t<i_t, f_t> csr_A(m, n, 0); |
| 280 | + csr_A.m = m; |
| 281 | + csr_A.n = n; |
| 282 | + csr_A.row_start = {0}; |
| 283 | + |
| 284 | + std::vector<qc_t> qcs = {qc}; |
| 285 | + |
| 286 | + simplex_solver_settings_t<i_t, f_t> settings; |
| 287 | + settings.barrier = true; |
| 288 | + settings.barrier_presolve = true; |
| 289 | + settings.dualize = 0; |
| 290 | + |
| 291 | + try { |
| 292 | + convert_quadratic_constraints_to_second_order_cones<i_t, f_t>(n, qcs, csr_A, user_problem); |
| 293 | + csr_A.to_compressed_col(user_problem.A); |
| 294 | + lp_solution_t<i_t, f_t> solution(user_problem.num_rows, user_problem.num_cols); |
| 295 | + (void)solve_linear_program_with_barrier(user_problem, settings, solution); |
| 296 | + FAIL() << "Expected ValidationError for cross-only indefinite Q"; |
| 297 | + } catch (const cuopt::logic_error& e) { |
| 298 | + EXPECT_EQ(e.get_error_type(), cuopt::error_type_t::ValidationError); |
| 299 | + EXPECT_THAT(e.what(), testing::HasSubstr("non-convex")); |
| 300 | + EXPECT_THAT(e.what(), testing::HasSubstr("q0")); |
| 301 | + } |
| 302 | +} |
| 303 | + |
217 | 304 | // Test: rank-deficient PSD Q (e.g., Q = v*v^T with v = [1, 1]) |
218 | 305 | // minimize x0 + x1 |
219 | 306 | // subject to (x0 + x1)^2 <= 4 (i.e., |x0 + x1| <= 2) |
|
0 commit comments