diff --git a/MANIFEST.in b/MANIFEST.in index 156d94d8..1ab86cd4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,3 +4,4 @@ include README.rst include requirements.txt include versioneer.py include optlang/_version.py +recursive-exclude slow_tests * diff --git a/optlang/cplex_interface.py b/optlang/cplex_interface.py index 9279d488..baac260f 100644 --- a/optlang/cplex_interface.py +++ b/optlang/cplex_interface.py @@ -43,74 +43,82 @@ Zero = S.Zero One = S.One -_CPLEX_STATUS_TO_STATUS = { - cplex.Cplex.solution.status.MIP_abort_feasible: interface.ABORTED, - cplex.Cplex.solution.status.MIP_abort_infeasible: interface.ABORTED, - cplex.Cplex.solution.status.MIP_dettime_limit_feasible: interface.TIME_LIMIT, - cplex.Cplex.solution.status.MIP_dettime_limit_infeasible: interface.TIME_LIMIT, - cplex.Cplex.solution.status.MIP_feasible: interface.FEASIBLE, - cplex.Cplex.solution.status.MIP_feasible_relaxed_inf: interface.SPECIAL, - cplex.Cplex.solution.status.MIP_feasible_relaxed_quad: interface.SPECIAL, - cplex.Cplex.solution.status.MIP_feasible_relaxed_sum: interface.SPECIAL, - cplex.Cplex.solution.status.MIP_infeasible: interface.INFEASIBLE, - cplex.Cplex.solution.status.MIP_infeasible_or_unbounded: interface.INFEASIBLE_OR_UNBOUNDED, - cplex.Cplex.solution.status.MIP_optimal: interface.OPTIMAL, - cplex.Cplex.solution.status.MIP_optimal_infeasible: interface.SPECIAL, - cplex.Cplex.solution.status.MIP_optimal_relaxed_inf: interface.SPECIAL, - cplex.Cplex.solution.status.MIP_optimal_relaxed_sum: interface.SPECIAL, - cplex.Cplex.solution.status.MIP_time_limit_feasible: interface.TIME_LIMIT, - cplex.Cplex.solution.status.MIP_time_limit_infeasible: interface.TIME_LIMIT, - cplex.Cplex.solution.status.MIP_unbounded: interface.UNBOUNDED, - cplex.Cplex.solution.status.abort_dettime_limit: interface.ABORTED, - cplex.Cplex.solution.status.abort_dual_obj_limit: interface.ABORTED, - cplex.Cplex.solution.status.abort_iteration_limit: interface.ABORTED, - cplex.Cplex.solution.status.abort_obj_limit: interface.ABORTED, - cplex.Cplex.solution.status.abort_primal_obj_limit: interface.ABORTED, - cplex.Cplex.solution.status.abort_relaxed: interface.ABORTED, - cplex.Cplex.solution.status.abort_time_limit: interface.TIME_LIMIT, - cplex.Cplex.solution.status.abort_user: interface.ABORTED, - cplex.Cplex.solution.status.conflict_abort_contradiction: interface.SPECIAL, - cplex.Cplex.solution.status.conflict_abort_dettime_limit: interface.SPECIAL, - cplex.Cplex.solution.status.conflict_abort_iteration_limit: interface.SPECIAL, - cplex.Cplex.solution.status.conflict_abort_memory_limit: interface.SPECIAL, - cplex.Cplex.solution.status.conflict_abort_node_limit: interface.SPECIAL, - cplex.Cplex.solution.status.conflict_abort_obj_limit: interface.SPECIAL, - cplex.Cplex.solution.status.conflict_abort_time_limit: interface.SPECIAL, - cplex.Cplex.solution.status.conflict_abort_user: interface.SPECIAL, - cplex.Cplex.solution.status.conflict_feasible: interface.SPECIAL, - cplex.Cplex.solution.status.conflict_minimal: interface.SPECIAL, - cplex.Cplex.solution.status.fail_feasible: interface.SPECIAL, - cplex.Cplex.solution.status.fail_feasible_no_tree: interface.SPECIAL, - cplex.Cplex.solution.status.fail_infeasible: interface.SPECIAL, - cplex.Cplex.solution.status.fail_infeasible_no_tree: interface.SPECIAL, - cplex.Cplex.solution.status.feasible: interface.FEASIBLE, - cplex.Cplex.solution.status.feasible_relaxed_inf: interface.SPECIAL, - cplex.Cplex.solution.status.feasible_relaxed_quad: interface.SPECIAL, - cplex.Cplex.solution.status.feasible_relaxed_sum: interface.SPECIAL, - cplex.Cplex.solution.status.first_order: interface.SPECIAL, - cplex.Cplex.solution.status.infeasible: interface.INFEASIBLE, - cplex.Cplex.solution.status.infeasible_or_unbounded: interface.INFEASIBLE_OR_UNBOUNDED, - cplex.Cplex.solution.status.mem_limit_feasible: interface.MEMORY_LIMIT, - cplex.Cplex.solution.status.mem_limit_infeasible: interface.MEMORY_LIMIT, - cplex.Cplex.solution.status.node_limit_feasible: interface.NODE_LIMIT, - cplex.Cplex.solution.status.node_limit_infeasible: interface.NODE_LIMIT, - cplex.Cplex.solution.status.num_best: interface.NUMERIC, - cplex.Cplex.solution.status.optimal: interface.OPTIMAL, - cplex.Cplex.solution.status.optimal_face_unbounded: interface.SPECIAL, - cplex.Cplex.solution.status.optimal_infeasible: interface.INFEASIBLE, - cplex.Cplex.solution.status.optimal_populated: interface.SPECIAL, - cplex.Cplex.solution.status.optimal_populated_tolerance: interface.SPECIAL, - cplex.Cplex.solution.status.optimal_relaxed_inf: interface.SPECIAL, - cplex.Cplex.solution.status.optimal_relaxed_quad: interface.SPECIAL, - cplex.Cplex.solution.status.optimal_relaxed_sum: interface.SPECIAL, - cplex.Cplex.solution.status.optimal_tolerance: interface.OPTIMAL, - cplex.Cplex.solution.status.populate_solution_limit: interface.SPECIAL, - cplex.Cplex.solution.status.solution_limit: interface.SPECIAL, - cplex.Cplex.solution.status.unbounded: interface.UNBOUNDED, - cplex.Cplex.solution.status.relaxation_unbounded: interface.UNBOUNDED, +_STATUS_MAP = { + 'MIP_abort_feasible': interface.ABORTED, + 'MIP_abort_infeasible': interface.ABORTED, + 'MIP_dettime_limit_feasible': interface.TIME_LIMIT, + 'MIP_dettime_limit_infeasible': interface.TIME_LIMIT, + 'MIP_feasible': interface.FEASIBLE, + 'MIP_feasible_relaxed_inf': interface.SPECIAL, + 'MIP_feasible_relaxed_quad': interface.SPECIAL, + 'MIP_feasible_relaxed_sum': interface.SPECIAL, + 'MIP_infeasible': interface.INFEASIBLE, + 'MIP_infeasible_or_unbounded': interface.INFEASIBLE_OR_UNBOUNDED, + 'MIP_optimal': interface.OPTIMAL, + 'MIP_optimal_infeasible': interface.SPECIAL, + 'MIP_optimal_relaxed_inf': interface.SPECIAL, + 'MIP_optimal_relaxed_sum': interface.SPECIAL, + 'MIP_time_limit_feasible': interface.TIME_LIMIT, + 'MIP_time_limit_infeasible': interface.TIME_LIMIT, + 'MIP_unbounded': interface.UNBOUNDED, + 'abort_dettime_limit': interface.ABORTED, + 'abort_dual_obj_limit': interface.ABORTED, + 'abort_iteration_limit': interface.ABORTED, + 'abort_obj_limit': interface.ABORTED, + 'abort_primal_obj_limit': interface.ABORTED, + 'abort_relaxed': interface.ABORTED, + 'abort_time_limit': interface.TIME_LIMIT, + 'abort_user': interface.ABORTED, + 'conflict_abort_contradiction': interface.SPECIAL, + 'conflict_abort_dettime_limit': interface.SPECIAL, + 'conflict_abort_iteration_limit': interface.SPECIAL, + 'conflict_abort_memory_limit': interface.SPECIAL, + 'conflict_abort_node_limit': interface.SPECIAL, + 'conflict_abort_obj_limit': interface.SPECIAL, + 'conflict_abort_time_limit': interface.SPECIAL, + 'conflict_abort_user': interface.SPECIAL, + 'conflict_feasible': interface.SPECIAL, + 'conflict_minimal': interface.SPECIAL, + 'fail_feasible': interface.SPECIAL, + 'fail_feasible_no_tree': interface.SPECIAL, + 'fail_infeasible': interface.SPECIAL, + 'fail_infeasible_no_tree': interface.SPECIAL, + 'feasible': interface.FEASIBLE, + 'feasible_relaxed_inf': interface.SPECIAL, + 'feasible_relaxed_quad': interface.SPECIAL, + 'feasible_relaxed_sum': interface.SPECIAL, + 'first_order': interface.SPECIAL, + 'infeasible': interface.INFEASIBLE, + 'infeasible_or_unbounded': interface.INFEASIBLE_OR_UNBOUNDED, + 'mem_limit_feasible': interface.MEMORY_LIMIT, + 'mem_limit_infeasible': interface.MEMORY_LIMIT, + 'node_limit_feasible': interface.NODE_LIMIT, + 'node_limit_infeasible': interface.NODE_LIMIT, + 'num_best': interface.NUMERIC, + 'optimal': interface.OPTIMAL, + 'optimal_face_unbounded': interface.SPECIAL, + 'optimal_infeasible': interface.INFEASIBLE, + 'optimal_populated': interface.SPECIAL, + 'optimal_populated_tolerance': interface.SPECIAL, + 'optimal_relaxed_inf': interface.SPECIAL, + 'optimal_relaxed_quad': interface.SPECIAL, + 'optimal_relaxed_sum': interface.SPECIAL, + 'optimal_tolerance': interface.OPTIMAL, + 'populate_solution_limit': interface.SPECIAL, + 'solution_limit': interface.SPECIAL, + 'unbounded': interface.UNBOUNDED, + 'relaxation_unbounded': interface.UNBOUNDED, + 'non-existing-status': 'Here for testing that missing statuses are handled.' # 102: interface.OPTIMAL # The same as cplex.Cplex.solution.status.optimal_tolerance } +# Check if each status is supported by the current cplex version +_CPLEX_STATUS_TO_STATUS = {} +for status_name, optlang_status in _STATUS_MAP.items(): + cplex_status = getattr(cplex.Cplex.solution.status, status_name, None) + if cplex_status is not None: + _CPLEX_STATUS_TO_STATUS[cplex_status] = optlang_status + _LP_METHODS = ["auto", "primal", "dual", "network", "barrier", "sifting", "concurrent"] _SOLUTION_TARGETS = ("auto", "convex", "local", "global") diff --git a/optlang/gurobi_interface.py b/optlang/gurobi_interface.py index e2ec220f..048aeb21 100644 --- a/optlang/gurobi_interface.py +++ b/optlang/gurobi_interface.py @@ -126,7 +126,8 @@ def lb(self, value): if value is None: value = -gurobipy.GRB.INFINITY if self.problem: - return self._internal_variable.setAttr('LB', value) + self._internal_variable.setAttr('LB', value) + self.problem.problem.update() @interface.Variable.ub.setter def ub(self, value): @@ -134,7 +135,8 @@ def ub(self, value): if value is None: value = gurobipy.GRB.INFINITY if self.problem: - return self._internal_variable.setAttr('UB', value) + self._internal_variable.setAttr('UB', value) + self.problem.problem.update() def set_bounds(self, lb, ub): super(Variable, self).set_bounds(lb, ub) @@ -188,6 +190,7 @@ def set_linear_coefficients(self, coefficients): grb_constraint = self.problem.problem.getConstrByName(self.name) for var, coeff in six.iteritems(coefficients): self.problem.problem.chgCoeff(grb_constraint, self.problem.problem.getVarByName(var.name), float(coeff)) + self.problem.update() else: raise Exception("Can't change coefficients if constraint is not associated with a model.") @@ -280,6 +283,7 @@ def _set_constraint_bounds(self, sense, rhs, range_value): self.problem.problem.update() self.problem.problem.addConstr(updated_row, sense, rhs, self.name) aux_var.setAttr("UB", range_value) + self.problem.update() @interface.Constraint.lb.setter def lb(self, value): @@ -326,9 +330,12 @@ def __init__(self, expression, sloppy=False, *args, **kwargs): @property def value(self): - try: - return self.problem.problem.getAttr("ObjVal") - except gurobipy.GurobiError: # TODO: let this check the actual error message + if getattr(self, "problem", None) is not None: + try: + return self.problem.problem.getAttr("ObjVal") + except gurobipy.GurobiError: # TODO: let this check the actual error message + return None + else: return None @interface.Objective.direction.setter @@ -339,12 +346,10 @@ def direction(self, value): def set_linear_coefficients(self, coefficients): if self.problem is not None: - grb_obj = self.problem.problem.getObjective() - for var, coeff in six.iteritems(coefficients): - grb_var = self.problem.problem.getVarByName(var.name) - grb_obj.remove(grb_var) - grb_obj.addTerms(float(coeff), grb_var) - self._expression_expired = True + for var, coeff in coefficients.items(): + var._internal_variable.setAttr("Obj", coeff) + self._expression_expired = True + self.problem.update() else: raise Exception("Can't change coefficients if objective is not associated with a model.") @@ -564,6 +569,8 @@ def update(self): super(Model, self).update(callback=self.problem.update) def _optimize(self): + if self.status != interface.OPTIMAL: + self.problem.reset() self.problem.update() self.problem.optimize() status = _GUROBI_STATUS_TO_STATUS[self.problem.getAttr("Status")] @@ -580,13 +587,12 @@ def _add_variables(self, variables): ub = gurobipy.GRB.INFINITY else: ub = variable.ub - gurobi_var = self.problem.addVar( + self.problem.addVar( name=variable.name, lb=lb, ub=ub, vtype=_VTYPE_TO_GUROBI_VTYPE[variable.type] ) - variable._internal_var = gurobi_var def _remove_variables(self, variables): # Not calling parent method to avoid expensive variable removal from sympy expressions @@ -611,17 +617,19 @@ def _add_constraints(self, constraints, sloppy=False): lhs = gurobipy.quicksum([coef * var._internal_variable for var, coef in coef_dict.items()]) sense, rhs, range_value = _constraint_lb_and_ub_to_gurobi_sense_rhs_and_range_value(constraint.lb, constraint.ub) - if range_value != 0.: + + if range_value != 0: aux_var = self.problem.addVar(name=constraint.name + '_aux', lb=0, ub=range_value) self.problem.update() lhs = lhs - aux_var - rhs = constraint.ub + self.problem.addConstr(lhs, sense, rhs, name=constraint.name) else: raise ValueError( "GUROBI currently only supports linear constraints. %s is not linear." % self) # self.problem.addQConstr(lhs, sense, rhs) constraint.problem = self + self.problem.update() def _remove_constraints(self, constraints): self.problem.update() diff --git a/optlang/tests/test_gurobi_interface.py b/optlang/tests/test_gurobi_interface.py index d9adcd8b..748d86aa 100644 --- a/optlang/tests/test_gurobi_interface.py +++ b/optlang/tests/test_gurobi_interface.py @@ -244,7 +244,7 @@ def test_gurobi_add_constraints(self): for i in range(row.size()): coeff_dict[row.getVar(i).VarName] = row.getCoeff(i) self.assertDictEqual(coeff_dict, {'x': 0.3, 'y': 0.4, 'z': 66., 'test_aux': -1.0}) - self.assertEqual(internal_constraint.RHS, 0) + self.assertEqual(internal_constraint.RHS, constr1.lb) self.assertEqual(self.model.problem.getVarByName(internal_constraint.getAttr('ConstrName') + '_aux'), 100) # constr2 coeff_dict = dict() @@ -253,7 +253,7 @@ def test_gurobi_add_constraints(self): for i in range(row.size()): coeff_dict[row.getVar(i).VarName] = row.getCoeff(i) self.assertDictEqual(coeff_dict, {'x': 2.333, 'y': 1.}) - self.assertEqual(internal_constraint.RHS, 96.997) + self.assertEqual(internal_constraint.RHS, constr2.ub) self.assertEqual(internal_constraint.Sense, '<') # constr3 coeff_dict = dict() @@ -263,7 +263,7 @@ def test_gurobi_add_constraints(self): for i in range(row.size()): coeff_dict[row.getVar(i).VarName] = row.getCoeff(i) self.assertDictEqual(coeff_dict, {'x': 2.333, 'y': 1., 'z': 1.}) - self.assertEqual(internal_constraint.RHS, -300) + self.assertEqual(internal_constraint.RHS, constr3.lb) self.assertEqual(internal_constraint.Sense, '>') # constr4 coeff_dict = dict() @@ -273,7 +273,7 @@ def test_gurobi_add_constraints(self): for i in range(row.size()): coeff_dict[row.getVar(i).VarName] = row.getCoeff(i) self.assertDictEqual(coeff_dict, {'x': 1}) - self.assertEqual(internal_constraint.RHS, -300) + self.assertEqual(internal_constraint.RHS, constr4.lb) self.assertEqual(internal_constraint.Sense, '=') def test_change_of_constraint_is_reflected_in_low_level_solver(self): diff --git a/slow_tests/README.md b/slow_tests/README.md new file mode 100644 index 00000000..1418a2a8 --- /dev/null +++ b/slow_tests/README.md @@ -0,0 +1,4 @@ +This directory contains long-running tests that should not +be packaged with optlang. `data/miplib2003` contains all +benchmark problems that can be solved <1 h with commercial +solvers. \ No newline at end of file diff --git a/slow_tests/__init__.py b/slow_tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/slow_tests/data/miplib2003.json b/slow_tests/data/miplib2003.json new file mode 100644 index 00000000..1b9f81bf --- /dev/null +++ b/slow_tests/data/miplib2003.json @@ -0,0 +1,74 @@ +{ + "10teams": { + "status": "optimal", + "solution": 924.0 + }, + "aflow30a": { + "status": "optimal", + "solution": 1158.0 + }, + "air05": { + "status": "optimal", + "solution": 26374.0 + }, + "arki001": { + "status": "optimal", + "solution": 7580810.0 + }, + "danoint": { + "status": "optimal", + "solution": 65.67 + }, + "disctom": { + "status": "optimal", + "solution": -5000.0 + }, + "fiber": { + "status": "optimal", + "solution": 405935.18 + }, + "fixnet6": { + "status": "optimal", + "solution": 3983.0 + }, + "gesa2": { + "status": "optimal", + "solution": 25779856.3719999976 + }, + "gesa2-o": { + "status": "optimal", + "solution": 25779856.3719999976 + }, + "glass4": { + "status": "optimal", + "solution": 1200010000.0 + }, + "harp2": { + "status": "optimal", + "solution": -73899800.0 + }, + "markshare1": { + "status": "optimal", + "solution": 1.0 + }, + "mas74": { + "status": "optimal", + "solution": 11801.2 + }, + "mas76": { + "status": "optimal", + "solution": 40005.1 + }, + "misc07": { + "status": "optimal", + "solution": 2810.0 + }, + "modglob": { + "status": "optimal", + "solution": 20740500.0 + }, + "momentum2": { + "status": "optimal", + "solution": 12314.2 + } +} \ No newline at end of file diff --git a/slow_tests/data/miplib2003/10teams.mps.gz b/slow_tests/data/miplib2003/10teams.mps.gz new file mode 100644 index 00000000..a4d5f2a7 Binary files /dev/null and b/slow_tests/data/miplib2003/10teams.mps.gz differ diff --git a/slow_tests/data/miplib2003/aflow30a.mps.gz b/slow_tests/data/miplib2003/aflow30a.mps.gz new file mode 100644 index 00000000..5bce4fdc Binary files /dev/null and b/slow_tests/data/miplib2003/aflow30a.mps.gz differ diff --git a/slow_tests/data/miplib2003/aflow40b.mps.gz b/slow_tests/data/miplib2003/aflow40b.mps.gz new file mode 100644 index 00000000..bbfa77eb Binary files /dev/null and b/slow_tests/data/miplib2003/aflow40b.mps.gz differ diff --git a/slow_tests/data/miplib2003/air04.mps.gz b/slow_tests/data/miplib2003/air04.mps.gz new file mode 100644 index 00000000..82b1390f Binary files /dev/null and b/slow_tests/data/miplib2003/air04.mps.gz differ diff --git a/slow_tests/data/miplib2003/air05.mps.gz b/slow_tests/data/miplib2003/air05.mps.gz new file mode 100644 index 00000000..285f439c Binary files /dev/null and b/slow_tests/data/miplib2003/air05.mps.gz differ diff --git a/slow_tests/data/miplib2003/arki001.mps.gz b/slow_tests/data/miplib2003/arki001.mps.gz new file mode 100644 index 00000000..c995c255 Binary files /dev/null and b/slow_tests/data/miplib2003/arki001.mps.gz differ diff --git a/slow_tests/data/miplib2003/cap6000.mps.gz b/slow_tests/data/miplib2003/cap6000.mps.gz new file mode 100644 index 00000000..bca4e4da Binary files /dev/null and b/slow_tests/data/miplib2003/cap6000.mps.gz differ diff --git a/slow_tests/data/miplib2003/danoint.mps.gz b/slow_tests/data/miplib2003/danoint.mps.gz new file mode 100644 index 00000000..2e00f13d Binary files /dev/null and b/slow_tests/data/miplib2003/danoint.mps.gz differ diff --git a/slow_tests/data/miplib2003/disctom.mps.gz b/slow_tests/data/miplib2003/disctom.mps.gz new file mode 100644 index 00000000..d82934b7 Binary files /dev/null and b/slow_tests/data/miplib2003/disctom.mps.gz differ diff --git a/slow_tests/data/miplib2003/fiber.mps.gz b/slow_tests/data/miplib2003/fiber.mps.gz new file mode 100644 index 00000000..52b8ffaa Binary files /dev/null and b/slow_tests/data/miplib2003/fiber.mps.gz differ diff --git a/slow_tests/data/miplib2003/fixnet6.mps.gz b/slow_tests/data/miplib2003/fixnet6.mps.gz new file mode 100644 index 00000000..67236712 Binary files /dev/null and b/slow_tests/data/miplib2003/fixnet6.mps.gz differ diff --git a/slow_tests/data/miplib2003/gesa2-o.mps.gz b/slow_tests/data/miplib2003/gesa2-o.mps.gz new file mode 100644 index 00000000..e48746fe Binary files /dev/null and b/slow_tests/data/miplib2003/gesa2-o.mps.gz differ diff --git a/slow_tests/data/miplib2003/gesa2.mps.gz b/slow_tests/data/miplib2003/gesa2.mps.gz new file mode 100644 index 00000000..ddef5a78 Binary files /dev/null and b/slow_tests/data/miplib2003/gesa2.mps.gz differ diff --git a/slow_tests/data/miplib2003/glass4.mps.gz b/slow_tests/data/miplib2003/glass4.mps.gz new file mode 100644 index 00000000..edbbd22b Binary files /dev/null and b/slow_tests/data/miplib2003/glass4.mps.gz differ diff --git a/slow_tests/data/miplib2003/harp2.mps.gz b/slow_tests/data/miplib2003/harp2.mps.gz new file mode 100644 index 00000000..373aec1a Binary files /dev/null and b/slow_tests/data/miplib2003/harp2.mps.gz differ diff --git a/slow_tests/data/miplib2003/manna81.mps.gz b/slow_tests/data/miplib2003/manna81.mps.gz new file mode 100644 index 00000000..2e52dd40 Binary files /dev/null and b/slow_tests/data/miplib2003/manna81.mps.gz differ diff --git a/slow_tests/data/miplib2003/markshare1.mps.gz b/slow_tests/data/miplib2003/markshare1.mps.gz new file mode 100644 index 00000000..ce0606c2 Binary files /dev/null and b/slow_tests/data/miplib2003/markshare1.mps.gz differ diff --git a/slow_tests/data/miplib2003/mas74.mps.gz b/slow_tests/data/miplib2003/mas74.mps.gz new file mode 100644 index 00000000..58c8dc91 Binary files /dev/null and b/slow_tests/data/miplib2003/mas74.mps.gz differ diff --git a/slow_tests/data/miplib2003/mas76.mps.gz b/slow_tests/data/miplib2003/mas76.mps.gz new file mode 100644 index 00000000..5c01e184 Binary files /dev/null and b/slow_tests/data/miplib2003/mas76.mps.gz differ diff --git a/slow_tests/data/miplib2003/misc07.mps.gz b/slow_tests/data/miplib2003/misc07.mps.gz new file mode 100644 index 00000000..e2dbf712 Binary files /dev/null and b/slow_tests/data/miplib2003/misc07.mps.gz differ diff --git a/slow_tests/data/miplib2003/mod011.mps.gz b/slow_tests/data/miplib2003/mod011.mps.gz new file mode 100644 index 00000000..2cc0a434 Binary files /dev/null and b/slow_tests/data/miplib2003/mod011.mps.gz differ diff --git a/slow_tests/data/miplib2003/modglob.mps.gz b/slow_tests/data/miplib2003/modglob.mps.gz new file mode 100644 index 00000000..bac36e70 Binary files /dev/null and b/slow_tests/data/miplib2003/modglob.mps.gz differ diff --git a/slow_tests/data/miplib2003/msc98-ip.mps.gz b/slow_tests/data/miplib2003/msc98-ip.mps.gz new file mode 100644 index 00000000..37407ee0 Binary files /dev/null and b/slow_tests/data/miplib2003/msc98-ip.mps.gz differ diff --git a/slow_tests/data/miplib2003/mzzv11.mps.gz b/slow_tests/data/miplib2003/mzzv11.mps.gz new file mode 100644 index 00000000..78f6f58f Binary files /dev/null and b/slow_tests/data/miplib2003/mzzv11.mps.gz differ diff --git a/slow_tests/data/miplib2003/mzzv42z.mps.gz b/slow_tests/data/miplib2003/mzzv42z.mps.gz new file mode 100644 index 00000000..deb5681c Binary files /dev/null and b/slow_tests/data/miplib2003/mzzv42z.mps.gz differ diff --git a/slow_tests/data/miplib2003/net12.mps.gz b/slow_tests/data/miplib2003/net12.mps.gz new file mode 100644 index 00000000..20c89948 Binary files /dev/null and b/slow_tests/data/miplib2003/net12.mps.gz differ diff --git a/slow_tests/data/miplib2003/noswot.mps.gz b/slow_tests/data/miplib2003/noswot.mps.gz new file mode 100644 index 00000000..f6900ceb Binary files /dev/null and b/slow_tests/data/miplib2003/noswot.mps.gz differ diff --git a/slow_tests/data/miplib2003/nsrand-ipx.mps.gz b/slow_tests/data/miplib2003/nsrand-ipx.mps.gz new file mode 100644 index 00000000..d54de523 Binary files /dev/null and b/slow_tests/data/miplib2003/nsrand-ipx.mps.gz differ diff --git a/slow_tests/data/miplib2003/opt1217.mps.gz b/slow_tests/data/miplib2003/opt1217.mps.gz new file mode 100644 index 00000000..aa229557 Binary files /dev/null and b/slow_tests/data/miplib2003/opt1217.mps.gz differ diff --git a/slow_tests/data/miplib2003/p2756.mps.gz b/slow_tests/data/miplib2003/p2756.mps.gz new file mode 100644 index 00000000..aef5c6cc Binary files /dev/null and b/slow_tests/data/miplib2003/p2756.mps.gz differ diff --git a/slow_tests/data/miplib2003/pk1.mps.gz b/slow_tests/data/miplib2003/pk1.mps.gz new file mode 100644 index 00000000..261e202f Binary files /dev/null and b/slow_tests/data/miplib2003/pk1.mps.gz differ diff --git a/slow_tests/data/miplib2003/pp08a.mps.gz b/slow_tests/data/miplib2003/pp08a.mps.gz new file mode 100644 index 00000000..444c17ee Binary files /dev/null and b/slow_tests/data/miplib2003/pp08a.mps.gz differ diff --git a/slow_tests/data/miplib2003/pp08aCUTS.mps.gz b/slow_tests/data/miplib2003/pp08aCUTS.mps.gz new file mode 100644 index 00000000..5a5ed5d2 Binary files /dev/null and b/slow_tests/data/miplib2003/pp08aCUTS.mps.gz differ diff --git a/slow_tests/data/miplib2003/qiu.mps.gz b/slow_tests/data/miplib2003/qiu.mps.gz new file mode 100644 index 00000000..1d23dab9 Binary files /dev/null and b/slow_tests/data/miplib2003/qiu.mps.gz differ diff --git a/slow_tests/data/miplib2003/roll3000.mps.gz b/slow_tests/data/miplib2003/roll3000.mps.gz new file mode 100644 index 00000000..2ef49b41 Binary files /dev/null and b/slow_tests/data/miplib2003/roll3000.mps.gz differ diff --git a/slow_tests/data/miplib2003/rout.mps.gz b/slow_tests/data/miplib2003/rout.mps.gz new file mode 100644 index 00000000..05143232 Binary files /dev/null and b/slow_tests/data/miplib2003/rout.mps.gz differ diff --git a/slow_tests/data/miplib2003/set1ch.mps.gz b/slow_tests/data/miplib2003/set1ch.mps.gz new file mode 100644 index 00000000..7d42cc25 Binary files /dev/null and b/slow_tests/data/miplib2003/set1ch.mps.gz differ diff --git a/slow_tests/data/miplib2003/timtab1.mps.gz b/slow_tests/data/miplib2003/timtab1.mps.gz new file mode 100644 index 00000000..245354e7 Binary files /dev/null and b/slow_tests/data/miplib2003/timtab1.mps.gz differ diff --git a/slow_tests/data/miplib2003/tr12-30.mps.gz b/slow_tests/data/miplib2003/tr12-30.mps.gz new file mode 100644 index 00000000..7025865b Binary files /dev/null and b/slow_tests/data/miplib2003/tr12-30.mps.gz differ diff --git a/slow_tests/data/miplib2003/vpm2.mps.gz b/slow_tests/data/miplib2003/vpm2.mps.gz new file mode 100644 index 00000000..f5733336 Binary files /dev/null and b/slow_tests/data/miplib2003/vpm2.mps.gz differ diff --git a/slow_tests/test_miplib_cplex_interface.py b/slow_tests/test_miplib_cplex_interface.py new file mode 100644 index 00000000..2959c4c8 --- /dev/null +++ b/slow_tests/test_miplib_cplex_interface.py @@ -0,0 +1,91 @@ +# Copyright 2014 Novo Nordisk Foundation Center for Biosustainability, DTU. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import gzip +import json +import os +import tempfile +import unittest +from functools import partial + +import nose + +try: + + import cplex + +except ImportError as e: + + raise nose.SkipTest('Skipping MILP tests because cplex is not available.') + +else: + from optlang.cplex_interface import Model + + # problems from http://miplib.zib.de/miplib2003/miplib2003.php + + TRAVIS = os.getenv('TRAVIS', False) + + DATA_PATH = os.path.join(os.path.dirname(__file__), 'data') + SOLUTION = os.path.join(DATA_PATH, "miplib2003.json") + PROBLEMS_DIR = os.path.join(DATA_PATH, "miplib2003") + + + def load_problem(mps_file): + prob_tmp_file = tempfile.mktemp(suffix='.mps') + with open(prob_tmp_file, 'wb') as tmp_handle: + f = gzip.open(mps_file, 'rb') + tmp_handle.write(f.read()) + f.close() + + problem = cplex.Cplex() + problem.read(prob_tmp_file) + model = Model(problem=problem) + model.configuration.presolve = True + model.configuration.timeout = 60 * 9 + return problem, model + + + def check_dimensions(model, cplex_problem): + nose.tools.assert_true(cplex_problem.variables.get_num() == len(model.variables)) + + + def check_optimization(model, expected_solution): + status = model.optimize() + if status is not "time_limit": + nose.tools.assert_equals(status, expected_solution['status']) + + if status is "optimal": + nose.tools.assert_almost_equal(expected_solution['solution'], model.objective.value, places=4) + + + def test_miplib(solutions=SOLUTION, problem_dir=PROBLEMS_DIR): + if TRAVIS: + raise nose.SkipTest('Skipping extensive MILP tests on travis-ci.') + with open(solutions, "r") as f: + data = json.load(f) + print(data) + for name, problem_data in data.items(): + problem_file = os.path.join(problem_dir, "{}.mps.gz".format(name)) + + glpk_problem, model = load_problem(problem_file) + func = partial(check_dimensions, model, glpk_problem) + func.description = "test_miplib_dimensions_%s (%s)" % (name, os.path.basename(str(__file__))) + yield func + + func = partial(check_optimization, model, problem_data) + func.description = "test_miplib_optimization_%s (%s)" % (name, os.path.basename(str(__file__))) + yield func + +if __name__ == '__main__': + nose.runmodule() diff --git a/slow_tests/test_miplib_glpk_interface.py b/slow_tests/test_miplib_glpk_interface.py new file mode 100644 index 00000000..fb16f8ca --- /dev/null +++ b/slow_tests/test_miplib_glpk_interface.py @@ -0,0 +1,85 @@ +# Copyright 2014 Novo Nordisk Foundation Center for Biosustainability, DTU. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import json +import gzip +import os +import tempfile +from functools import partial + +import nose +from swiglpk import glp_read_mps, GLP_MPS_FILE, glp_create_prob, glp_get_num_cols + +from optlang.glpk_interface import Model + +# problems from http://miplib.zib.de/miplib2003/miplib2003.php + +TRAVIS = os.getenv('TRAVIS', False) + +DATA_PATH = os.path.join(os.path.dirname(__file__), 'data') +SOLUTION = os.path.join(DATA_PATH, "miplib2003.json") +PROBLEMS_DIR = os.path.join(DATA_PATH, "miplib2003") + + +def load_problem(mps_file): + prob_tmp_file = tempfile.mktemp(suffix='.mps') + with open(prob_tmp_file, 'wb') as tmp_handle: + f = gzip.open(mps_file, 'rb') + tmp_handle.write(f.read()) + f.close() + + problem = glp_create_prob() + glp_read_mps(problem, GLP_MPS_FILE, None, prob_tmp_file) + model = Model(problem=problem) + model.configuration.presolve = True + model.configuration.verbosity = 3 + model.configuration.timeout = 60 * 9 + return problem, model + + +def check_dimensions(model, glpk_problem): + nose.tools.assert_true(glp_get_num_cols(glpk_problem) == len(model.variables)) + + +def check_optimization(model, expected_solution): + status = model.optimize() + if status is not "time_limit": + nose.tools.assert_equals(status, expected_solution['status']) + + if status is "optimal": + nose.tools.assert_almost_equal(expected_solution['solution'], model.objective.value, places=4) + + +def test_miplib(solutions=SOLUTION, problem_dir=PROBLEMS_DIR): + if TRAVIS: + raise nose.SkipTest('Skipping extensive MILP tests on travis-ci.') + with open(solutions, "r") as f: + data = json.load(f) + print(data) + for name, problem_data in data.items(): + problem_file = os.path.join(problem_dir, "{}.mps.gz".format(name)) + + glpk_problem, model = load_problem(problem_file) + func = partial(check_dimensions, model, glpk_problem) + func.description = "test_miplib_dimensions_%s (%s)" % (name, os.path.basename(str(__file__))) + yield func + + func = partial(check_optimization, model, problem_data) + func.description = "test_miplib_optimization_%s (%s)" % (name, os.path.basename(str(__file__))) + yield func + + +if __name__ == '__main__': + nose.runmodule() diff --git a/slow_tests/test_miplib_gurobi_interface.py b/slow_tests/test_miplib_gurobi_interface.py new file mode 100644 index 00000000..280bf586 --- /dev/null +++ b/slow_tests/test_miplib_gurobi_interface.py @@ -0,0 +1,91 @@ +# Copyright 2014 Novo Nordisk Foundation Center for Biosustainability, DTU. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import gzip +import json +import os +import tempfile +import unittest +from functools import partial + +import nose + +try: + + import gurobipy + +except ImportError as e: + + raise nose.SkipTest('Skipping MILP tests because gurobi is not available.') + +else: + from optlang.gurobi_interface import Model + + # problems from http://miplib.zib.de/miplib2003/miplib2003.php + + TRAVIS = os.getenv('TRAVIS', False) + + DATA_PATH = os.path.join(os.path.dirname(__file__), 'data') + SOLUTION = os.path.join(DATA_PATH, "miplib2003.json") + PROBLEMS_DIR = os.path.join(DATA_PATH, "miplib2003") + + + def load_problem(mps_file): + prob_tmp_file = tempfile.mktemp(suffix='.mps') + with open(prob_tmp_file, 'wb') as tmp_handle: + f = gzip.open(mps_file, 'rb') + tmp_handle.write(f.read()) + f.close() + + problem = gurobipy.read(prob_tmp_file) + model = Model(problem=problem) + model.configuration.presolve = True + model.configuration.verbosity = 3 + model.configuration.timeout = 60 * 9 + return problem, model + + + def check_dimensions(model, gurobi_problem): + nose.tools.assert_true(len(gurobi_problem.getVars()) == len(model.variables)) + + + def check_optimization(model, expected_solution): + status = model.optimize() + if status is not "time_limit": + nose.tools.assert_equals(status, expected_solution['status']) + + if status is "optimal": + nose.tools.assert_almost_equal(expected_solution['solution'], model.objective.value, places=4) + + + def test_miplib(solutions=SOLUTION, problem_dir=PROBLEMS_DIR): + if TRAVIS: + raise nose.SkipTest('Skipping extensive MILP tests on travis-ci.') + with open(solutions, "r") as f: + data = json.load(f) + print(data) + for name, problem_data in data.items(): + problem_file = os.path.join(problem_dir, "{}.mps.gz".format(name)) + + problem, model = load_problem(problem_file) + func = partial(check_dimensions, model, problem) + func.description = "test_miplib_dimensions_%s (%s)" % (name, os.path.basename(str(__file__))) + yield func + + func = partial(check_optimization, model, problem_data) + func.description = "test_miplib_optimization_%s (%s)" % (name, os.path.basename(str(__file__))) + yield func + +if __name__ == '__main__': + nose.runmodule()