Skip to content
Snippets Groups Projects
Commit dd4dec19 authored by Hannah Ritter's avatar Hannah Ritter
Browse files

addid MIP solver

parent f3aa1a1a
No related branches found
No related tags found
No related merge requests found
...@@ -2,6 +2,12 @@ import pulp ...@@ -2,6 +2,12 @@ import pulp
from typing import List, Tuple, Dict from typing import List, Tuple, Dict
import numpy as np import numpy as np
import abc import abc
import mip
import logging
import timeit
import argparse
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
class LPSolver(abc.ABC): class LPSolver(abc.ABC):
...@@ -29,6 +35,41 @@ class LPSolver(abc.ABC): ...@@ -29,6 +35,41 @@ class LPSolver(abc.ABC):
def _set_costs(self, edges): def _set_costs(self, edges):
pass pass
class MipLPSolver(LPSolver):
def __init__(self, edges, num_verts_a, num_verts_b):
self.model = mip.Model(sense=mip.MINIMIZE, solver_name=mip.CBC)
self.variables = self._make_variables(num_verts_a, num_verts_b)
self._make_constraints(num_verts_a, num_verts_b)
self._set_costs(edges)
def solve(self):
return self.model.optimize()
def _make_variables(self, num_verts_a, num_verts_b):
return {
(i, j): self.model.add_var(name=f"y_{i}_{j}", var_type=mip.BINARY)
for i in range(num_verts_a + 1, num_verts_a + num_verts_b + 1)
for j in range(num_verts_a + 1, num_verts_a + num_verts_b + 1) if i < j
}
def _set_costs(self, edges):
self.model.objective = mip.minimize(
mip.xsum(
self.variables[(l, j)] if k > i and j > l
else 1 - self.variables[(j, l)] if l > j
else 0
for (i, j) in edges
for (k, l) in edges
)
)
def _make_constraints(self, n0, n1):
for i in range(n0 + 1, n0 + n1 - 1):
for j in range(i + 1, n0 + n1):
for k in range(j + 1, n0 + n1 + 1):
self.model.add_constr(self.variables[(i, k)] >= self.variables[(i, j)] + self.variables[(j, k)] - 1)
class PulpLPSolver(LPSolver): class PulpLPSolver(LPSolver):
def __init__(self, edges, constraints, num_verts_a, num_verts_b): def __init__(self, edges, constraints, num_verts_a, num_verts_b):
...@@ -94,68 +135,33 @@ class PulpLPSolver(LPSolver): ...@@ -94,68 +135,33 @@ class PulpLPSolver(LPSolver):
self.problem += costs[(i, j, k, l)] == self.variables[(l, j)] self.problem += costs[(i, j, k, l)] == self.variables[(l, j)]
elif l > j: elif l > j:
self.problem += costs[(i, j, k, l)] == 1 - self.variables[(j, l)] self.problem += costs[(i, j, k, l)] == 1 - self.variables[(j, l)]
def _cut(constraints: Dict[Tuple], constraints_pool: Dict[Tuple]) -> Dict[Tuple]:
"""
Adds cutting planes: Adds constraints from constraints_pool to constraints.
"""
new_constraints = constraints.copy()
return new_constraints
def _branch(solved_problem: pulp.LpProblem, variables: Dict[Tuple]):
"""
Branches on a binary variable: Sets a relaxed variable to either 0 or 1.
"""
pass
def _parse_graph_file() -> Tuple[List[Tuple]]:
pass
def solve(edges: List[Tuple]):
"""
Solves an ILP using Branch-and-Cut.
"""
edges, constraints, num_verts_a, num_verts_b = _parse_graph_file()
problem = PulpLPSolver(edges, constraints, num_verts_a, num_verts_b)
problems = [problem]
x_star = None
v_star = -np.Inf
while len(problems) > 0:
constraints_violated = True
current_problem = problems.pop(0)
# this while loop is necessary for step 6
while constraints_violated:
current_problem.solve()
# problem infeasible, go to next problem in queue def _parse_graph_file(graph_file) -> Tuple[List[Tuple]]:
if current_problem.is_problem_infeasible(): edges = []
break with open(graph_file, "r") as file:
for line in file:
if current_problem.is_solution_optimal(): if line.startswith('c'):
# not an improvement, go to next problem in queue continue
if pulp.value(current_problem) >= v_star: elif line.startswith('p'):
break parts = line.split()
# current solution improves the value of the solution, continue evaluation of the solution n0 = int(parts[2])
else: n1 = int(parts[3])
# step 5 logging.info(f"Größen der Partitionen: A={n0}, B={n1}")
# check if solution is integer: if yes, set current solution as best solution else:
integer_variables, fractional_variables = current_problem.get_integer_and_fractional_variables_of_solution() x, y = map(int, line.split())
if len(fractional_variables) == 0: edges.append((x, y))
v_star = pulp.value(current_problem) return edges, n0, n1
x_star = current_problem
continue
# step 6
# get constraints that are fulfilled by the integer vars but violated by the fractional ones
violated_constraints = _get_violated_constraints(integer_variables, fractional_variables)
# add the violated constraints back to the LP and go back to the inner while loop
if len(violated_constraints) > 0:
current_problem += violated_constraints
else:
constraints_violated = False
def solve(filepath):
mipsolver = MipLPSolver(*_parse_graph_file(filepath))
print(timeit.timeit(mipsolver.solve, number=1))
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("file")
args = parser.parse_args()
solve(args.file)
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment