Skip to content
Snippets Groups Projects
Commit 1bb4174b authored by konog98's avatar konog98
Browse files

Weitere kleinere Anpassungen.

parent 2f27f6ee
Branches pace-2024
Tags
1 merge request!3Pace 2024
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
This project is a STUDENT SUBMISSION and contains a solver for the PACE 2024 challenge. The solver aims to minimize crossings in bipartite graphs using linear programming and various optimization techniques. This project is a STUDENT SUBMISSION and contains a solver for the PACE 2024 challenge. The solver aims to minimize crossings in bipartite graphs using linear programming and various optimization techniques.
Our approach first attempts to minimize crossings by assigning a truth value for each crossing. Due to this simplistic restriction, cycles can occur in the B partition (y). We then systematically try to reduce these cycles by adding constraints. Attempting to add all constraints initially sets too many constraints for the ILP solver, making it infeasible. However, the current solution is memory and runtime intensive.
From the medium_test_sets, only the following graphs run within the allotted time: 8.gr, 9.gr, 10.gr, 11.gr, 14.gr, 40.gr, 48.gr, 49.gr, 52.gr.
## Setup ## Setup
1. **Install Python**: go to [python.org](https://wiki.python.org/moin/BeginnersGuide/Download) 1. **Install Python**: go to [python.org](https://wiki.python.org/moin/BeginnersGuide/Download)
...@@ -53,13 +57,13 @@ This project requires the following external libraries: ...@@ -53,13 +57,13 @@ This project requires the following external libraries:
To run the solver with an input file, use the following command: To run the solver with an input file, use the following command:
```bash ```bash
Get-Content path/to/input_file.gr | python solver_opt.py Get-Content path/to/input_file.gr | python solver.py
``` ```
Example: Example:
```bash ```bash
Get-Content githubtests/tiny_test_set/instances/complete_4_5.gr | python solver_opt.py Get-Content githubtests/tiny_test_set/instances/complete_4_5.gr | python solver.py
``` ```
## Verifier and Tester in PowerShell ## Verifier and Tester in PowerShell
......
Entscheidungsvariablen:
x ij zur Angabe der Reihenfolge zwischen i und j in Menge A.
y ij zur Angabe der Reihenfolge zwischen i und j in Menge B.
c ijkl, um anzugeben, ob es eine Kreuzung zwischen den Kanten (i, j) und (k, l) gibt.
xij = 1 bedeutet, dass π1(i) < π1(j) — Knoten i kommt vor Knoten j in der ersten Permutation.
yij = 1 bedeutet, dass π2(i) < π2(j) — Knoten i kommt vor Knoten j in der zweiten Permutation.
main: logged nur das wesentliche
backupmain: aktuelles backup
foretesting: gleiches wie main, nur mit konsolen output, was nur für kleine graphen etwas bringt.
solver.py: Funktion die 2 Argumente übernimmt, Inputgraph und Outputgraph. Wird für den pace2024tester genutzt.
solver.bat: Script für windwos, welches die solver.py mit den beiden Konsolen Inputs ausführt.
\ No newline at end of file
File moved
File moved
...@@ -3,6 +3,7 @@ import sys ...@@ -3,6 +3,7 @@ import sys
from pulp import * from pulp import *
from collections import defaultdict, deque from collections import defaultdict, deque
# Count the number of crossings from the crossing variables
def count_crossings_via_variables(c_vars): def count_crossings_via_variables(c_vars):
crossings = 0 crossings = 0
for c_var in c_vars.values(): for c_var in c_vars.values():
...@@ -10,11 +11,12 @@ def count_crossings_via_variables(c_vars): ...@@ -10,11 +11,12 @@ def count_crossings_via_variables(c_vars):
crossings += 1 crossings += 1
return crossings return crossings
# Prioritize cycles based on their length (example: shorter cycles first)
def prioritize_cycles(cycles): def prioritize_cycles(cycles):
# Beispiel: Zyklen nach Länge priorisieren
cycles.sort(key=len, reverse=False) cycles.sort(key=len, reverse=False)
return cycles return cycles
# Detect cycles in the graph using depth-first search (DFS)
def detect_cycle(graph, node, visited, rec_stack, path, cycles): def detect_cycle(graph, node, visited, rec_stack, path, cycles):
visited[node] = True visited[node] = True
rec_stack[node] = True rec_stack[node] = True
...@@ -30,6 +32,7 @@ def detect_cycle(graph, node, visited, rec_stack, path, cycles): ...@@ -30,6 +32,7 @@ def detect_cycle(graph, node, visited, rec_stack, path, cycles):
rec_stack[node] = False rec_stack[node] = False
path.pop() path.pop()
# Find all unique cycles in the graph
def find_all_cycles(graph, nodes): def find_all_cycles(graph, nodes):
visited = {node: False for node in nodes} visited = {node: False for node in nodes}
rec_stack = {node: False for node in nodes} rec_stack = {node: False for node in nodes}
...@@ -40,7 +43,7 @@ def find_all_cycles(graph, nodes): ...@@ -40,7 +43,7 @@ def find_all_cycles(graph, nodes):
if not visited[node]: if not visited[node]:
detect_cycle(graph, node, visited, rec_stack, path, cycles) detect_cycle(graph, node, visited, rec_stack, path, cycles)
# Entferne doppelte Zyklen # Remove duplicate cycles
unique_cycles = [] unique_cycles = []
seen = set() seen = set()
for cycle in cycles: for cycle in cycles:
...@@ -51,6 +54,7 @@ def find_all_cycles(graph, nodes): ...@@ -51,6 +54,7 @@ def find_all_cycles(graph, nodes):
return unique_cycles return unique_cycles
# Add constraints to the linear programming problem based on the detected cycles
def add_cycle_constraints(prob, y, cycles, added_constraints): def add_cycle_constraints(prob, y, cycles, added_constraints):
for cycle in cycles: for cycle in cycles:
cycle = list(dict.fromkeys(cycle)) cycle = list(dict.fromkeys(cycle))
...@@ -58,7 +62,7 @@ def add_cycle_constraints(prob, y, cycles, added_constraints): ...@@ -58,7 +62,7 @@ def add_cycle_constraints(prob, y, cycles, added_constraints):
for j in range(i + 1, len(cycle)): for j in range(i + 1, len(cycle)):
constraint_1 = ((cycle[i], cycle[j]), (cycle[j], cycle[i])) constraint_1 = ((cycle[i], cycle[j]), (cycle[j], cycle[i]))
if constraint_1 not in added_constraints: if constraint_1 not in added_constraints:
# einer von beiden ist wahr # One of the two must be true
prob += y[(cycle[i], cycle[j])] + y[(cycle[j], cycle[i])] == 1 prob += y[(cycle[i], cycle[j])] + y[(cycle[j], cycle[i])] == 1
added_constraints.add(constraint_1) added_constraints.add(constraint_1)
...@@ -66,10 +70,11 @@ def add_cycle_constraints(prob, y, cycles, added_constraints): ...@@ -66,10 +70,11 @@ def add_cycle_constraints(prob, y, cycles, added_constraints):
if (cycle[i], cycle[j]) in y and (cycle[j], cycle[k]) in y and (cycle[i], cycle[k]) in y: if (cycle[i], cycle[j]) in y and (cycle[j], cycle[k]) in y and (cycle[i], cycle[k]) in y:
constraint_2 = ((cycle[i], cycle[j]), (cycle[j], cycle[k]), (cycle[i], cycle[k])) constraint_2 = ((cycle[i], cycle[j]), (cycle[j], cycle[k]), (cycle[i], cycle[k]))
if constraint_2 not in added_constraints: if constraint_2 not in added_constraints:
#i < j und j < l -> i< k reihenfolgen constraint transitivität # i < j and j < k -> i < k (transitivity constraint)
prob += 0 <= y[(cycle[i], cycle[j])] + y[(cycle[j], cycle[k])] <= 1 + y[(cycle[i], cycle[k])] prob += 0 <= y[(cycle[i], cycle[j])] + y[(cycle[j], cycle[k])] <= 1 + y[(cycle[i], cycle[k])]
added_constraints.add(constraint_2) added_constraints.add(constraint_2)
# Solve the bipartite minimization problem
def solve_bipartite_minimization(input_lines): def solve_bipartite_minimization(input_lines):
edges = [] edges = []
for line in input_lines: for line in input_lines:
...@@ -82,33 +87,39 @@ def solve_bipartite_minimization(input_lines): ...@@ -82,33 +87,39 @@ def solve_bipartite_minimization(input_lines):
else: else:
x, y = map(int, line.split()) x, y = map(int, line.split())
edges.append((x, y)) edges.append((x, y))
logging.info(f"{len(edges)} Kanten geladen.") logging.info(f"{len(edges)} edges loaded.")
# Define the linear programming problem
prob = LpProblem("Minimize_Crossings", LpMinimize) prob = LpProblem("Minimize_Crossings", LpMinimize)
# Define binary variables for the edges
y = {(i, j): LpVariable(f"y_{i}_{j}", 0, 1, cat='Binary') for i in range(n0 + 1, n0 + n1 + 1) for j in range(n0 + 1, n0 + n1 + 1) if i != j} y = {(i, j): LpVariable(f"y_{i}_{j}", 0, 1, cat='Binary') for i in range(n0 + 1, n0 + n1 + 1) for j in range(n0 + 1, n0 + n1 + 1) if i != j}
# Define binary variables for crossings
c = {(i, j, k, l): LpVariable(f"c_{i}_{j}_{k}_{l}", 0, 1, cat='Binary') for (i, j) in edges for (k, l) in edges} c = {(i, j, k, l): LpVariable(f"c_{i}_{j}_{k}_{l}", 0, 1, cat='Binary') for (i, j) in edges for (k, l) in edges}
# Objective function: minimize the sum of crossings
prob += lpSum(c.values()) prob += lpSum(c.values())
# Add constraints to the problem
for (i, j) in edges: for (i, j) in edges:
for (k, l) in edges: for (k, l) in edges:
if k > i: if k > i:
if j > l: if j > l:
prob += c[(i, j, k, l)] == y[(l, j)] prob += c[(i, j, k, l)] == y[(l, j)]
elif l > j: elif l > j:
prob += c[(i, j, k, l)] == y[(l, j)] prob += c[(i, j, k, l)] == 1 - y[(j, l)]
added_constraints = set() added_constraints = set()
iteration = 0 # The maximum cycle length at which the elimination of all cycles begins
max_cycle_length = 9 max_cycle_length = 9
while True: while True:
iteration += 1
prob.solve(PULP_CBC_CMD(msg=0)) prob.solve(PULP_CBC_CMD(msg=0))
if prob.status != LpStatusOptimal: if prob.status != LpStatusOptimal:
break break
# Build the graph based on the current solution
graph = defaultdict(list) graph = defaultdict(list)
in_degree = defaultdict(int) in_degree = defaultdict(int)
nodes = range(n0 + 1, n0 + n1 + 1) nodes = range(n0 + 1, n0 + n1 + 1)
...@@ -124,25 +135,27 @@ def solve_bipartite_minimization(input_lines): ...@@ -124,25 +135,27 @@ def solve_bipartite_minimization(input_lines):
graph[j].append(i) graph[j].append(i)
in_degree[i] += 1 in_degree[i] += 1
# Detect all cycles in the graph
all_cycles = find_all_cycles(graph, nodes) all_cycles = find_all_cycles(graph, nodes)
if not all_cycles: if not all_cycles:
break break
# Prioritize the cycles by their length
prioritized_cycles = prioritize_cycles(all_cycles) prioritized_cycles = prioritize_cycles(all_cycles)
# Berechne k basierend auf dem aktuellen Prozentsatz # Check the length of the longest cycle and adjust k accordingly
# Überprüfe die Länge des längsten Zyklus und passe k entsprechend an
if len(prioritized_cycles[-1]) > max_cycle_length: if len(prioritized_cycles[-1]) > max_cycle_length:
k = min(int(math.floor(len(prioritized_cycles)/4)), len(prioritized_cycles)) k = min(int(math.floor(len(prioritized_cycles)/4)), len(prioritized_cycles))
else: else:
k = len(prioritized_cycles) k = len(prioritized_cycles)
# Nehme nur die ersten k Zyklen aus der priorisierten Liste
# Take only the first k cycles from the prioritized list
cycles_to_process = prioritized_cycles[:k] cycles_to_process = prioritized_cycles[:k]
add_cycle_constraints(prob, y, cycles_to_process, added_constraints) add_cycle_constraints(prob, y, cycles_to_process, added_constraints)
if prob.status == LpStatusOptimal: if prob.status == LpStatusOptimal:
# Topologically sort the nodes based on the in-degree
zero_in_degree_queue = deque([i for i in nodes if in_degree[i] == 0]) zero_in_degree_queue = deque([i for i in nodes if in_degree[i] == 0])
sorted_b = [] sorted_b = []
while zero_in_degree_queue: while zero_in_degree_queue:
...@@ -153,11 +166,11 @@ def solve_bipartite_minimization(input_lines): ...@@ -153,11 +166,11 @@ def solve_bipartite_minimization(input_lines):
if in_degree[neighbor] == 0: if in_degree[neighbor] == 0:
zero_in_degree_queue.append(neighbor) zero_in_degree_queue.append(neighbor)
a = count_crossings_via_variables(c)
return sorted_b return sorted_b
else: else:
return return None
# Main function to read input and solve the problem
def main(): def main():
input_lines = sys.stdin.read().strip().split('\n') input_lines = sys.stdin.read().strip().split('\n')
result = solve_bipartite_minimization(input_lines) result = solve_bipartite_minimization(input_lines)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment