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
from typing import List, Tuple, Dict
import numpy as np
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):
......@@ -29,6 +35,41 @@ class LPSolver(abc.ABC):
def _set_costs(self, edges):
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):
def __init__(self, edges, constraints, num_verts_a, num_verts_b):
......@@ -94,68 +135,33 @@ class PulpLPSolver(LPSolver):
self.problem += costs[(i, j, k, l)] == self.variables[(l, j)]
elif l > j:
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
if current_problem.is_problem_infeasible():
break
if current_problem.is_solution_optimal():
# not an improvement, go to next problem in queue
if pulp.value(current_problem) >= v_star:
break
# current solution improves the value of the solution, continue evaluation of the solution
else:
# step 5
# check if solution is integer: if yes, set current solution as best solution
integer_variables, fractional_variables = current_problem.get_integer_and_fractional_variables_of_solution()
if len(fractional_variables) == 0:
v_star = pulp.value(current_problem)
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 _parse_graph_file(graph_file) -> Tuple[List[Tuple]]:
edges = []
with open(graph_file, "r") as file:
for line in file:
if line.startswith('c'):
continue
elif line.startswith('p'):
parts = line.split()
n0 = int(parts[2])
n1 = int(parts[3])
logging.info(f"Größen der Partitionen: A={n0}, B={n1}")
else:
x, y = map(int, line.split())
edges.append((x, y))
return edges, n0, n1
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