From 1bb4174b684cc9d2c99272b38f65019e44c5f4c6 Mon Sep 17 00:00:00 2001
From: konog98 <konog98@mi.fu-berlin.de>
Date: Mon, 10 Jun 2024 00:03:43 +0200
Subject: [PATCH] Weitere kleinere Anpassungen.

---
 README.md                                     |  8 +++-
 src/README.md                                 | 17 -------
 .../medium_test_set/instances}/11.gr          |  0
 .../medium_test_set/solutions}/11.sol         |  0
 src/solver.py                                 | 45 ++++++++++++-------
 5 files changed, 35 insertions(+), 35 deletions(-)
 delete mode 100644 src/README.md
 rename src/{ => githubtests/medium_test_set/instances}/11.gr (100%)
 rename src/{ => githubtests/medium_test_set/solutions}/11.sol (100%)

diff --git a/README.md b/README.md
index dd0739b..4b0f9f3 100644
--- a/README.md
+++ b/README.md
@@ -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.
 
+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
 
 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:
     To run the solver with an input file, use the following command:
 
     ```bash
-    Get-Content path/to/input_file.gr | python solver_opt.py
+    Get-Content path/to/input_file.gr | python solver.py
     ```
 
     Example:
 
     ```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
diff --git a/src/README.md b/src/README.md
deleted file mode 100644
index 66911e8..0000000
--- a/src/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-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
diff --git a/src/11.gr b/src/githubtests/medium_test_set/instances/11.gr
similarity index 100%
rename from src/11.gr
rename to src/githubtests/medium_test_set/instances/11.gr
diff --git a/src/11.sol b/src/githubtests/medium_test_set/solutions/11.sol
similarity index 100%
rename from src/11.sol
rename to src/githubtests/medium_test_set/solutions/11.sol
diff --git a/src/solver.py b/src/solver.py
index c858917..e757788 100644
--- a/src/solver.py
+++ b/src/solver.py
@@ -3,6 +3,7 @@ import sys
 from pulp import *
 from collections import defaultdict, deque
 
+# Count the number of crossings from the crossing variables
 def count_crossings_via_variables(c_vars):
     crossings = 0
     for c_var in c_vars.values():
@@ -10,11 +11,12 @@ def count_crossings_via_variables(c_vars):
             crossings += 1
     return crossings
 
+# Prioritize cycles based on their length (example: shorter cycles first)
 def prioritize_cycles(cycles):
-    # Beispiel: Zyklen nach Länge priorisieren
     cycles.sort(key=len, reverse=False)
     return cycles
 
+# Detect cycles in the graph using depth-first search (DFS)
 def detect_cycle(graph, node, visited, rec_stack, path, cycles):
     visited[node] = True
     rec_stack[node] = True
@@ -30,6 +32,7 @@ def detect_cycle(graph, node, visited, rec_stack, path, cycles):
     rec_stack[node] = False
     path.pop()
 
+# Find all unique cycles in the graph
 def find_all_cycles(graph, nodes):
     visited = {node: False for node in nodes}
     rec_stack = {node: False for node in nodes}
@@ -40,7 +43,7 @@ def find_all_cycles(graph, nodes):
         if not visited[node]:
             detect_cycle(graph, node, visited, rec_stack, path, cycles)
 
-    # Entferne doppelte Zyklen
+    # Remove duplicate cycles
     unique_cycles = []
     seen = set()
     for cycle in cycles:
@@ -51,6 +54,7 @@ def find_all_cycles(graph, nodes):
 
     return unique_cycles
 
+# Add constraints to the linear programming problem based on the detected cycles
 def add_cycle_constraints(prob, y, cycles, added_constraints):
     for cycle in cycles:
         cycle = list(dict.fromkeys(cycle))
@@ -58,7 +62,7 @@ def add_cycle_constraints(prob, y, cycles, added_constraints):
             for j in range(i + 1, len(cycle)):
                 constraint_1 = ((cycle[i], cycle[j]), (cycle[j], cycle[i]))
                 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
                     added_constraints.add(constraint_1)
                     
@@ -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:
                         constraint_2 = ((cycle[i], cycle[j]), (cycle[j], cycle[k]), (cycle[i], cycle[k]))
                         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])]
                             added_constraints.add(constraint_2)
 
+# Solve the bipartite minimization problem
 def solve_bipartite_minimization(input_lines):
     edges = []
     for line in input_lines:
@@ -82,33 +87,39 @@ def solve_bipartite_minimization(input_lines):
         else:
             x, y = map(int, line.split())
             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)
+    
+    # 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}
+    
+    # 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}
 
+    # Objective function: minimize the sum of crossings
     prob += lpSum(c.values())
 
+    # Add constraints to the problem
     for (i, j) in edges:
         for (k, l) in edges:
             if k > i:
                 if j > l:
                     prob += c[(i, j, k, l)] == y[(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()
-    iteration = 0
+    # The maximum cycle length at which the elimination of all cycles begins
     max_cycle_length = 9
 
     while True:
-        iteration += 1
         prob.solve(PULP_CBC_CMD(msg=0))
-
         if prob.status != LpStatusOptimal:
             break
 
+        # Build the graph based on the current solution
         graph = defaultdict(list)
         in_degree = defaultdict(int)
         nodes = range(n0 + 1, n0 + n1 + 1)
@@ -124,25 +135,27 @@ def solve_bipartite_minimization(input_lines):
                             graph[j].append(i)
                             in_degree[i] += 1
 
+        # Detect all cycles in the graph
         all_cycles = find_all_cycles(graph, nodes)
         
         if not all_cycles:
             break
 
+        # Prioritize the cycles by their length
         prioritized_cycles = prioritize_cycles(all_cycles)
 
-        # Berechne k basierend auf dem aktuellen Prozentsatz
-        # Überprüfe die Länge des längsten Zyklus und passe k entsprechend an
+        # Check the length of the longest cycle and adjust k accordingly
         if len(prioritized_cycles[-1]) > max_cycle_length:
             k = min(int(math.floor(len(prioritized_cycles)/4)), len(prioritized_cycles))
         else:
             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]
         add_cycle_constraints(prob, y, cycles_to_process, added_constraints)
 
     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])
         sorted_b = []
         while zero_in_degree_queue:
@@ -153,11 +166,11 @@ def solve_bipartite_minimization(input_lines):
                 if in_degree[neighbor] == 0:
                     zero_in_degree_queue.append(neighbor)
 
-        a = count_crossings_via_variables(c)
         return sorted_b
     else:
-        return
-    
+        return None
+
+# Main function to read input and solve the problem
 def main():
     input_lines = sys.stdin.read().strip().split('\n')
     result = solve_bipartite_minimization(input_lines)
-- 
GitLab