diff --git a/cpp/src/mip/presolve/gf2_presolve.cpp b/cpp/src/mip/presolve/gf2_presolve.cpp index c396a12c1..60e36e89d 100644 --- a/cpp/src/mip/presolve/gf2_presolve.cpp +++ b/cpp/src/mip/presolve/gf2_presolve.cpp @@ -37,6 +37,12 @@ namespace cuopt::linear_programming::detail { +template +static inline i_t positive_modulo(i_t i, i_t n) +{ + return (i % n + n) % n; +} + // this is kind-of a stopgap implementation (as in practice MIPLIB2017 only contains a couple of GF2 // problems and they're small) but cuDSS could be used for this since A is likely to be sparse and // low-bandwidth (i think?) unlikely to occur in real-world problems however. doubt it'd be worth @@ -178,7 +184,7 @@ papilo::PresolveStatus GF2Presolve::execute(const papilo::Problem& pro gf2_constraints.emplace_back((size_t)cstr_idx, std::move(constraint_bin_vars), std::pair{key_var_idx, key_var_coeff}, - ((size_t)rhs) % 2); + positive_modulo((int)rhs, 2)); continue; not_valid: continue; diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index ab5699aff..457737c08 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -118,6 +118,9 @@ papilo::Problem build_papilo_problem(const optimization_problem_t if (!h_var_lb.empty() && !h_var_ub.empty()) { builder.setColLbAll(h_var_lb); builder.setColUbAll(h_var_ub); + if (op_problem.get_variable_names().size() == h_var_lb.size()) { + builder.setColNameAll(op_problem.get_variable_names()); + } } for (size_t i = 0; i < h_var_types.size(); ++i) { diff --git a/cpp/tests/mip/termination_test.cu b/cpp/tests/mip/termination_test.cu index 90221d3f1..7e405a8ad 100644 --- a/cpp/tests/mip/termination_test.cu +++ b/cpp/tests/mip/termination_test.cu @@ -116,6 +116,18 @@ TEST(termination_status, crossing_bounds_infeasible) EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible); } +TEST(termination_status, gf2_presolve_optimal) +{ + auto [termination_status, obj_val, lb] = test_mps_file("mip/enlight_hard.mps", 0.5, true); + EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); +} + +TEST(termination_status, gf2_presolve_infeasible) +{ + auto [termination_status, obj_val, lb] = test_mps_file("mip/enlight11.mps", 0.5, true); + EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible); +} + TEST(termination_status, bb_infeasible_test) { // First, check that presolve doesn't reduce the problem to infeasibility diff --git a/datasets/mip/download_miplib_test_dataset.sh b/datasets/mip/download_miplib_test_dataset.sh index 1adc3a784..872462da4 100755 --- a/datasets/mip/download_miplib_test_dataset.sh +++ b/datasets/mip/download_miplib_test_dataset.sh @@ -34,6 +34,7 @@ INSTANCES=( "neos5" "swath1" "enlight_hard" + "enlight11" "supportcase22" ) diff --git a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py index 7d3c550e2..272a2f205 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py @@ -159,6 +159,9 @@ def test_time_limit_solver(): settings.set_optimality_tolerance(1e-12) time_limit_seconds = 0.2 settings.set_parameter(CUOPT_TIME_LIMIT, time_limit_seconds) + # Solver mode isn't what's tested here. + # Set it to Stable2 as CI is more reliable with this mode + settings.set_parameter(CUOPT_PDLP_SOLVER_MODE, PDLPSolverMode.Stable2) # Setting both to make sure the lowest one is picked settings.set_parameter(CUOPT_ITERATION_LIMIT, 99999999)