1616import linopy
1717from linopy import Model
1818from linopy .constraints import QuadraticConstraint
19- from linopy .solvers import available_solvers , quadratic_constraint_solvers
19+ from linopy .solvers import (
20+ available_solvers ,
21+ nonconvex_quadratic_constraint_solvers ,
22+ quadratic_constraint_solvers ,
23+ )
2024
2125# Build parameter list: (solver, io_api) for QC-capable solvers
2226qc_solver_params : list [tuple [str , str ]] = []
2630 if solver in ["gurobi" , "mosek" ]:
2731 qc_solver_params .append ((solver , "direct" ))
2832
33+ # Build parameter list for nonconvex QC tests
34+ nonconvex_qc_solver_params : list [tuple [str , str ]] = []
35+ for solver in nonconvex_quadratic_constraint_solvers :
36+ if solver in available_solvers :
37+ nonconvex_qc_solver_params .append ((solver , "lp" ))
38+ if solver == "gurobi" :
39+ nonconvex_qc_solver_params .append ((solver , "direct" ))
40+
2941
3042@pytest .fixture
3143def m () -> Model :
@@ -1020,15 +1032,11 @@ def test_qc_multidim_solution(
10201032 assert np .allclose (y_vals , 4.472 , atol = 0.01 )
10211033 assert np .isclose (obj_val , 3 * 11.18 , atol = 0.05 ) # 3x single solution
10221034
1023- @pytest .mark .parametrize ("solver,io_api" , qc_solver_params )
1035+ @pytest .mark .parametrize ("solver,io_api" , nonconvex_qc_solver_params )
10241036 def test_qc_mixed_linear_quad (
10251037 self , qc_mixed_model : Model , solver : str , io_api : str
10261038 ) -> None :
1027- """Test QC with both quadratic and linear terms."""
1028- # COPT does not handle this nonconvex constraint (x-1)² <= 0
1029- if solver == "copt" :
1030- pytest .skip ("COPT does not support this nonconvex constraint form" )
1031-
1039+ """Test QC with both quadratic and linear terms (nonconvex)."""
10321040 status , condition = qc_mixed_model .solve (solver , io_api = io_api )
10331041 assert status == "ok"
10341042 assert condition == "optimal"
@@ -1037,15 +1045,11 @@ def test_qc_mixed_linear_quad(
10371045 x_val = float (qc_mixed_model .solution ["x" ].values )
10381046 assert np .isclose (x_val , 1.0 , atol = 0.01 )
10391047
1040- @pytest .mark .parametrize ("solver,io_api" , qc_solver_params )
1048+ @pytest .mark .parametrize ("solver,io_api" , nonconvex_qc_solver_params )
10411049 def test_qc_cross_terms (
10421050 self , qc_cross_terms_model : Model , solver : str , io_api : str
10431051 ) -> None :
10441052 """Test QC with cross product terms (xy) - nonconvex bilinear."""
1045- # MOSEK and COPT do not support nonconvex problems
1046- if solver in ("mosek" , "copt" ):
1047- pytest .skip (f"{ solver .upper ()} does not support nonconvex bilinear constraints" )
1048-
10491053 status , condition = qc_cross_terms_model .solve (solver , io_api = io_api )
10501054 assert status == "ok"
10511055 assert condition == "optimal"
@@ -1062,15 +1066,11 @@ def test_qc_cross_terms(
10621066 # Verify optimal objective value
10631067 assert np .isclose (obj_val , 5.0 , atol = 0.01 )
10641068
1065- @pytest .mark .parametrize ("solver,io_api" , qc_solver_params )
1069+ @pytest .mark .parametrize ("solver,io_api" , nonconvex_qc_solver_params )
10661070 def test_qc_geq_constraint (
10671071 self , qc_geq_model : Model , solver : str , io_api : str
10681072 ) -> None :
10691073 """Test >= quadratic constraint - nonconvex."""
1070- # MOSEK and COPT do not support nonconvex problems
1071- if solver in ("mosek" , "copt" ):
1072- pytest .skip (f"{ solver .upper ()} does not support nonconvex >= quadratic constraints" )
1073-
10741074 status , condition = qc_geq_model .solve (solver , io_api = io_api )
10751075 assert status == "ok"
10761076 assert condition == "optimal"
@@ -1087,15 +1087,11 @@ def test_qc_geq_constraint(
10871087 # Verify optimal objective value
10881088 assert np .isclose (obj_val , 2.0 , atol = 0.01 )
10891089
1090- @pytest .mark .parametrize ("solver,io_api" , qc_solver_params )
1090+ @pytest .mark .parametrize ("solver,io_api" , nonconvex_qc_solver_params )
10911091 def test_qc_equality_constraint (
10921092 self , qc_equality_model : Model , solver : str , io_api : str
10931093 ) -> None :
10941094 """Test = quadratic constraint - nonconvex equality."""
1095- # MOSEK does not support nonlinear equality constraints
1096- if solver == "mosek" :
1097- pytest .skip ("MOSEK does not support nonlinear equality constraints" )
1098-
10991095 status , condition = qc_equality_model .solve (solver , io_api = io_api )
11001096 assert status == "ok"
11011097 assert condition == "optimal"
0 commit comments