diff --git a/ilpy/impl/solvers/GurobiBackend.cpp b/ilpy/impl/solvers/GurobiBackend.cpp index 9faf868..e8f63c8 100644 --- a/ilpy/impl/solvers/GurobiBackend.cpp +++ b/ilpy/impl/solvers/GurobiBackend.cpp @@ -218,21 +218,31 @@ GurobiBackend::solve(Solution& x, std::string& msg) { GRB_CHECK(GRBupdatemodel(_model)); + GRBenv* modelenv = GRBgetenv(_model); + if (_timeout > 0) { - GRBenv* modelenv = GRBgetenv(_model); GRB_CHECK(GRBsetdblparam(modelenv, GRB_DBL_PAR_TIMELIMIT, _timeout)); } if (_gap >= 0) { - GRBenv* modelenv = GRBgetenv(_model); if (_absoluteGap) GRB_CHECK(GRBsetdblparam(modelenv, GRB_DBL_PAR_MIPGAPABS, _gap)); else GRB_CHECK(GRBsetdblparam(modelenv, GRB_DBL_PAR_MIPGAP, _gap)); } + // Sets the strategy for handling non-convex quadratic objectives + // or non-convex quadratic constraints. + // 0 = an error is reported if the original user model contains non-convex + // quadratic constructs. + // 1 = an error is reported if non-convex quadratic constructs could not be + // discarded or linearized during presolve. + // 2 = non-convex quadratic problems are solved by means of translating them + // into bilinear form and applying spatial branching. + GRB_CHECK(GRBsetintparam(modelenv, GRB_INT_PAR_NONCONVEX, 2)); + GRB_CHECK(GRBoptimize(_model)); int status; diff --git a/tests/test_solvers.py b/tests/test_solvers.py index 40af4d8..47cb8a4 100644 --- a/tests/test_solvers.py +++ b/tests/test_solvers.py @@ -13,11 +13,11 @@ HAVE_GUROBI = False +PREFS = [ilpy.Preference.Scip, pytest.param(ilpy.Preference.Gurobi, marks=gu_marks)] + + @pytest.mark.parametrize("as_expression", [True, False], ids=["as_expr", "as_constr"]) -@pytest.mark.parametrize( - "preference", - [ilpy.Preference.Scip, pytest.param(ilpy.Preference.Gurobi, marks=gu_marks)], -) +@pytest.mark.parametrize("preference", PREFS) def test_simple_solver(preference: ilpy.Preference, as_expression: bool) -> None: num_vars = 10 special_var = 5 @@ -62,10 +62,7 @@ def test_simple_solver(preference: ilpy.Preference, as_expression: bool) -> None @pytest.mark.parametrize("as_expression", [True, False], ids=["as_expr", "as_constr"]) -@pytest.mark.parametrize( - "preference", - [ilpy.Preference.Scip, pytest.param(ilpy.Preference.Gurobi, marks=gu_marks)], -) +@pytest.mark.parametrize("preference", PREFS) def test_quadratic_solver(preference: ilpy.Preference, as_expression: bool) -> None: num_vars = 10 special_var = 5 @@ -105,15 +102,13 @@ def test_quadratic_solver(preference: ilpy.Preference, as_expression: bool) -> N assert solution[5] == -2 # jan please check -@pytest.mark.skipif(not HAVE_GUROBI, reason="Gurobi not installed") -def test_non_convex_quadratic_gurobi() -> None: +@pytest.mark.parametrize("preference", PREFS) +def test_non_convex_quadratic(preference: ilpy.Preference) -> None: # currently, just a smoke test to make sure we don't crash on solve. obj = ilpy.Objective() obj.set_quadratic_coefficient(0, 0, -1) # quadratic term (-x^2) - solver = ilpy.Solver( - 1, ilpy.VariableType.Continuous, preference=ilpy.Preference.Gurobi - ) + solver = ilpy.Solver(1, ilpy.VariableType.Continuous, preference=preference) solver.set_objective(obj) constraint = ilpy.Constraint() @@ -121,6 +116,5 @@ def test_non_convex_quadratic_gurobi() -> None: constraint.set_value(1) solver.add_constraint(constraint) - # Gurobi will raise an exception at the moment... may be changed later: - with pytest.raises(RuntimeError, match="Q matrix is not positive semi-definite"): - solver.solve() + # Gurobi will give zeros and SCIP will give something like -9999999987 + assert solver.solve() is not None