Skip to content
Snippets Groups Projects
Commit ad3fa566 authored by Dominik Krupke's avatar Dominik Krupke
Browse files

comments by Gabriel, added DBST code (german comments)

parent 1c4932f8
No related branches found
No related tags found
No related merge requests found
from ortools.sat.python import cp_model
import itertools
class DBSTSolverCP:
def __squared_distance(self, vi, wi):
"""
Gegeben zwei Punkt-Indizes, berechne die (ganzzahlige)
quadrierte Distanz zwischen den Punkten.
"""
v, w = self.points[vi], self.points[wi]
return (v[0] - w[0])**2 + (v[1] - w[1])**2
def __compute_distances(self):
"""
Berechne eine Matrix der Distanzen.
"""
self.distances = {(v,w): self.__squared_distance(v, w) for v,w in\
itertools.product(range(self.n), range(self.n))}
self.max_distance = max(self.distances.values())
def __make_vars(self):
"""
Erzeuge die Variablen und setze die Zielfunktion.
"""
self.edge_vars = {(v,w): self.model.NewBoolVar(f'x_{v},{w}') for v,w in itertools.combinations(range(self.n), 2)}
self.edge_vars.update({(w,v): self.model.NewBoolVar(f'x_{w},{v}') for v,w in self.edge_vars})
self.depth_vars = {v: self.model.NewIntVar(0, self.n-1, f'd_{v}') for v in range(self.n)}
self.bottleneck_var = self.model.NewIntVar(0, self.max_distance, 'b')
self.model.Minimize(self.bottleneck_var)
def __add_degree_constraints(self):
"""
Füge die Gradbedingungen hinzu.
"""
v0out = 0
for v in range(1, self.n):
self.model.Add(self.edge_vars[v,0] == 0) # x_vv0 = 0
v0out += self.edge_vars[0,v]
self.model.Add(v0out <= self.max_degree) # Grad d für v0
for v in range(1, self.n):
vin, vout = 0, 0
for w in range(0, self.n):
if v != w:
vin += self.edge_vars[w,v]
vout += self.edge_vars[v,w]
self.model.Add(vin == 1) # genau 1 eingehende Kante für v
self.model.Add(vout <= self.max_degree - 1) # <= d-1 ausgehende Kanten für v
def __forbid_bidirectional_edges(self):
"""
Füge die (streng genommen redundanten) Constraints x_{v,w} -> !x_{w, v} hinzu.
"""
for v,w in itertools.combinations(range(self.n), 2):
self.model.AddBoolOr([self.edge_vars[v,w].Not(), self.edge_vars[w,v].Not()])
def __add_depth_constraints(self):
"""
Füge die Tiefenconstraints x_{v,w} -> d_w = d_v + 1 hinzu.
"""
self.model.Add(self.depth_vars[0] == 0)
all_edges = 0
for (v, w), x_vw in self.edge_vars.items():
self.model.Add(self.depth_vars[w] == self.depth_vars[v] + 1).OnlyEnforceIf(x_vw)
all_edges += x_vw
self.model.Add(all_edges == self.n-1)
def __add_bottleneck_constraints(self):
"""
Füge die Constraints b >= d(v,w) * x_{v,w} hinzu.
"""
for (v, w), x_vw in self.edge_vars.items():
self.model.Add(self.bottleneck_var >= self.distances[v,w] * x_vw)
def __init__(self, points, max_degree):
"""
Erzeuge das Modell.
:param points: Liste der Punkte (ganzzahlig, in der Ebene, als (x,y)-Tupel).
:param max_degree: Der höchste zulässige Grad in unserem Spannbaum.
"""
self.points = points
self.n = len(self.points)
self.max_degree = max_degree
self.model = cp_model.CpModel()
self.__compute_distances()
self.__make_vars()
self.__forbid_bidirectional_edges()
self.__add_degree_constraints()
self.__add_depth_constraints()
self.__add_bottleneck_constraints()
def solve(self):
"""
Suche die optimale Lösung für das konstruierte Modell und gebe eine Lösung
(Liste von Kanten als ((x1,y1),(x2,y2))-Tupel) zurück.
"""
solver = cp_model.CpSolver()
status = solver.Solve(self.model)
if status != cp_model.OPTIMAL:
raise RuntimeError("Unexpected status after running solver!")
return [(self.points[v], self.points[w]) for (v,w),x_vw in self.edge_vars.items() if solver.Value(x_vw) != 0]
\ No newline at end of file
This diff is collapsed.
......@@ -137,9 +137,9 @@ Both problems are NP-hard and solutions often look similar, but they can differ
Your task in this sheet is to solve the problem with CP-SAT.
1. Model the BTSP as a Constraint Program by stating the necessary variables, the objective function, and the constraints.
1. Model the BTSP as a Constraint Program by stating the necessary variables, the objective function, and the constraints. This should be similiar to the DBST example.
2. Implement this model using CP-SAT. You can reuse code from the DBST-solver.
3. Create some tests based on handcrafted instances with known unique optimal solution.
3. Create some tests based on handcrafted instances for which you can argue the optimal value.
4. Create a benchmark with at least 100 instances of various sizes to evaluate the performance of your solver and to allow comparisons with other solver implementations (by checking, which solver can solve more instances with a timelimit of one minute per instance). You can use random positions but adding some structure and symmetry can yield more difficult instances.
5. Create a version of your solver that enforces a single tour via assigning vertices the corresponing index as variable, with $x_{vw}=1 \Rightarrow index(w)=index(v)+1$ (except for the origin), and one that uses CP-SAT's [`AddCircuit` constraint](https://google.github.io/or-tools/python/ortools/sat/python/cp_model.html#CpModel.AddCircuit). Compare both versions with your benchmark.
6. Give an sound estimate on how well this problem can be solved with CP-SAT based on your experiments. Up to which size would you consider this problem to be easily solvable?
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment