11"""Tests for Python-MIP"""
2+ import math
23from itertools import product
3- import pytest
4+ from os import environ
5+
46import networkx as nx
7+ import mip .gurobi
58import mip .highs
69from mip import Model , xsum , OptimizationStatus , MAXIMIZE , BINARY , INTEGER
7- from mip import ConstrsGenerator , CutPool , maximize , CBC , GUROBI , HIGHS , Column
10+ from mip import ConstrsGenerator , CutPool , maximize , CBC , GUROBI , HIGHS , Column , Constr
811from os import environ
12+ from util import skip_on
913import math
14+ import pytest
1015
1116TOL = 1e-4
1217
1318SOLVERS = [CBC ]
14- if "GUROBI_HOME" in environ :
19+ if mip . gurobi . has_gurobi and "GUROBI_HOME" in environ :
1520 SOLVERS += [GUROBI ]
1621if mip .highs .has_highs :
1722 SOLVERS += [HIGHS ]
1823
1924
25+ @skip_on (NotImplementedError )
2026@pytest .mark .parametrize ("solver" , SOLVERS )
2127def test_column_generation (solver : str ):
2228 L = 250 # bar length
@@ -86,6 +92,7 @@ def test_column_generation(solver: str):
8692 assert round (master .objective_value ) == 3
8793
8894
95+ @skip_on (NotImplementedError )
8996@pytest .mark .parametrize ("solver" , SOLVERS )
9097def test_cutting_stock (solver : str ):
9198 n = 10 # maximum number of bars
@@ -122,6 +129,7 @@ def test_cutting_stock(solver: str):
122129 assert sum (x .x for x in model .vars ) >= 5
123130
124131
132+ @skip_on (NotImplementedError )
125133@pytest .mark .parametrize ("solver" , SOLVERS )
126134def test_knapsack (solver : str ):
127135 p = [10 , 13 , 18 , 31 , 7 , 15 ]
@@ -153,6 +161,7 @@ def test_knapsack(solver: str):
153161 assert abs (m .objective .expr [x [1 ]] - 28 ) <= 1e-10
154162
155163
164+ @skip_on (NotImplementedError )
156165@pytest .mark .parametrize ("solver" , SOLVERS )
157166def test_queens (solver : str ):
158167 """MIP model n-queens"""
@@ -207,6 +216,7 @@ def test_queens(solver: str):
207216 assert rows_with_queens == n # "feasible solution"
208217
209218
219+ @skip_on (NotImplementedError )
210220@pytest .mark .parametrize ("solver" , SOLVERS )
211221def test_tsp (solver : str ):
212222 """tsp related tests"""
@@ -312,6 +322,7 @@ def generate_constrs(self, model: Model, depth: int = 0, npass: int = 0):
312322 model .add_cut (cut )
313323
314324
325+ @skip_on (NotImplementedError )
315326@pytest .mark .parametrize ("solver" , SOLVERS )
316327def test_tsp_cuts (solver : str ):
317328 """tsp related tests"""
@@ -383,9 +394,8 @@ def test_tsp_cuts(solver: str):
383394 assert abs (m .objective_value - 262 ) <= TOL # "mip model objective"
384395
385396
386- # Exclude HiGHS solver, which doesn't support MIP start.
387- SOLVERS_WITH_MIPSTART = [s for s in SOLVERS if s != HIGHS ]
388- @pytest .mark .parametrize ("solver" , SOLVERS_WITH_MIPSTART )
397+ @skip_on (NotImplementedError )
398+ @pytest .mark .parametrize ("solver" , SOLVERS )
389399def test_tsp_mipstart (solver : str ):
390400 """tsp related tests"""
391401 N = ["a" , "b" , "c" , "d" , "e" , "f" , "g" ]
@@ -568,6 +578,7 @@ def test_obj_const2(self, solver: str):
568578 assert model .objective_const == 1
569579
570580
581+ @skip_on (NotImplementedError )
571582@pytest .mark .parametrize ("val" , range (1 , 4 ))
572583@pytest .mark .parametrize ("solver" , SOLVERS )
573584def test_variable_bounds (solver : str , val : int ):
@@ -583,6 +594,7 @@ def test_variable_bounds(solver: str, val: int):
583594 assert round (y .x ) == val
584595
585596
597+ @skip_on (NotImplementedError )
586598@pytest .mark .parametrize ("val" , range (1 , 4 ))
587599@pytest .mark .parametrize ("solver" , SOLVERS )
588600def test_linexpr_x (solver : str , val : int ):
@@ -611,6 +623,7 @@ def test_linexpr_x(solver: str, val: int):
611623 assert abs ((x + 2 * y + x + 1 + x / 2 ).x - (x .x + 2 * y .x + x .x + 1 + x .x / 2 )) < TOL
612624
613625
626+ @skip_on (NotImplementedError )
614627@pytest .mark .parametrize ("solver" , SOLVERS )
615628def test_add_column (solver : str ):
616629 """Simple test which add columns in a specific way"""
@@ -640,6 +653,7 @@ def test_add_column(solver: str):
640653 assert x in example_constr1 .expr .expr
641654
642655
656+ @skip_on (NotImplementedError )
643657@pytest .mark .parametrize ("val" , range (1 , 4 ))
644658@pytest .mark .parametrize ("solver" , SOLVERS )
645659def test_float (solver : str , val : int ):
@@ -658,3 +672,39 @@ def test_float(solver: str, val: int):
658672 assert y .x == float (y )
659673 # test linear expressions.
660674 assert float (x + y ) == (x + y ).x
675+
676+
677+ @skip_on (NotImplementedError )
678+ @pytest .mark .parametrize ("solver" , SOLVERS )
679+ def test_empty_useless_constraint_is_considered (solver : str ):
680+ m = Model ("empty_constraint" , solver_name = solver )
681+ x = m .add_var (name = "x" )
682+ y = m .add_var (name = "y" )
683+ m .add_constr (xsum ([]) <= 1 , name = "c_empty" ) # useless, empty constraint
684+ m .add_constr (x + y <= 5 , name = "c1" )
685+ m .add_constr (2 * x + y <= 6 , name = "c2" )
686+ m .objective = maximize (x + 2 * y )
687+ m .optimize ()
688+ # check objective
689+ assert m .status == OptimizationStatus .OPTIMAL
690+ assert abs (m .objective .x - 10 ) < TOL
691+ # check that all names of constraints could be queried
692+ assert {c .name for c in m .constrs } == {"c1" , "c2" , "c_empty" }
693+ assert all (isinstance (m .constr_by_name (c_name ), Constr ) for c_name in ("c1" , "c2" , "c_empty" ))
694+
695+
696+ @skip_on (NotImplementedError )
697+ @pytest .mark .parametrize ("solver" , SOLVERS )
698+ def test_empty_contradictory_constraint_is_considered (solver : str ):
699+ m = Model ("empty_constraint" , solver_name = solver )
700+ x = m .add_var (name = "x" )
701+ y = m .add_var (name = "y" )
702+ m .add_constr (xsum ([]) <= - 1 , name = "c_contra" ) # contradictory empty constraint
703+ m .add_constr (x + y <= 5 , name = "c1" )
704+ m .objective = maximize (x + 2 * y )
705+ m .optimize ()
706+ # assert infeasibility of problem
707+ assert m .status in (OptimizationStatus .INF_OR_UNBD , OptimizationStatus .INFEASIBLE )
708+ # check that all names of constraints could be queried
709+ assert {c .name for c in m .constrs } == {"c1" , "c_contra" }
710+ assert all (isinstance (m .constr_by_name (c_name ), Constr ) for c_name in ("c1" , "c_contra" ))
0 commit comments