From eea755b796f77ab5b9bb8aa917c974aa29f8f68e Mon Sep 17 00:00:00 2001
From: konog98 <konog98@mi.fu-berlin.de>
Date: Thu, 20 Jun 2024 00:29:28 +0200
Subject: [PATCH] =?UTF-8?q?Letzte=20Optimierung,=20opt2=20version=20mit=20?=
 =?UTF-8?q?kombination=20der=20Grad=201=20nodes=20f=C3=BCr=20den=20warm=20?=
 =?UTF-8?q?start?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/diff.py                                   |  48 +++
 .../medium_test_set/{instances => }/10.gr     |   0
 .../medium_test_set/{solutions => }/10.sol    |   0
 .../medium_test_set/{instances => }/11.gr     |   0
 .../medium_test_set/{solutions => }/11.sol    |   0
 src/mytests/solutions/12.sol                  | 124 ++++++++
 src/mytests/solutions/falsch12.sol            |   3 +
 src/mytests/solutions/korrekt12.sol           |   3 +
 ...t_besserer_zykel_betrachtung_und_cache.log |  14 +
 src/solver2.bat                               |   3 +
 src/solver_knoten_grad_1_2_try.py             | 249 +++++++++++++++
 src/solver_knoten_grad_1_einsortieren_opt.py  |  63 ++--
 src/solver_knoten_grad_1_einsortieren_opt2.py | 300 ++++++++++++++++++
 src/solver_opt.py                             |   3 +-
 14 files changed, 787 insertions(+), 23 deletions(-)
 create mode 100644 src/diff.py
 rename src/githubtests/medium_test_set/{instances => }/10.gr (100%)
 rename src/githubtests/medium_test_set/{solutions => }/10.sol (100%)
 rename src/githubtests/medium_test_set/{instances => }/11.gr (100%)
 rename src/githubtests/medium_test_set/{solutions => }/11.sol (100%)
 create mode 100644 src/mytests/solutions/12.sol
 create mode 100644 src/mytests/solutions/falsch12.sol
 create mode 100644 src/mytests/solutions/korrekt12.sol
 create mode 100644 src/optimiert_mit_besserer_zykel_betrachtung_und_cache.log
 create mode 100644 src/solver2.bat
 create mode 100644 src/solver_knoten_grad_1_2_try.py
 create mode 100644 src/solver_knoten_grad_1_einsortieren_opt2.py

diff --git a/src/diff.py b/src/diff.py
new file mode 100644
index 0000000..a87914c
--- /dev/null
+++ b/src/diff.py
@@ -0,0 +1,48 @@
+def read_file(filename):
+    with open(filename, 'r') as file:
+        lines = [line.strip().strip('[]').split(',') for line in file.read().splitlines()]
+    return [[int(node.strip()) for node in line] for line in lines]
+
+def find_all_violations(sublist, combined_list):
+    combined_index = {node: i for i, node in enumerate(combined_list)}
+    violations = []
+    for i in range(len(sublist) - 1):
+        if sublist[i] not in combined_index or sublist[i + 1] not in combined_index:
+            violations.append((sublist[i], sublist[i + 1], "Node not found in combined list"))
+        elif combined_index[sublist[i]] > combined_index[sublist[i + 1]]:
+            violations.append((sublist[i], sublist[i + 1], "Order violation"))
+    return violations
+
+def check_order_preservation(correct_lists):
+    if len(correct_lists) < 3:
+        print("The file does not contain three lists.")
+        return
+    
+    list1 = correct_lists[0]
+    list2 = correct_lists[1]
+    combined_list = correct_lists[2]
+
+    list1_violations = find_all_violations(list1, combined_list)
+    list2_violations = find_all_violations(list2, combined_list)
+
+    if not list1_violations and not list2_violations:
+        print("The order of nodes in the first two lists is preserved in the third list.")
+    else:
+        if list1_violations:
+            print("The order of nodes in the first list is not preserved in the third list:")
+            for node1, node2, reason in list1_violations:
+                print(f"Node {node1} should come before Node {node2}. Reason: {reason}")
+        if list2_violations:
+            print("The order of nodes in the second list is not preserved in the third list:")
+            for node1, node2, reason in list2_violations:
+                print(f"Node {node1} should come before Node {node2}. Reason: {reason}")
+
+def main():
+    correct_file = 'mytests/solutions/korrekt12.sol'
+
+    correct_lists = read_file(correct_file)
+
+    check_order_preservation(correct_lists)
+
+if __name__ == "__main__":
+    main()
diff --git a/src/githubtests/medium_test_set/instances/10.gr b/src/githubtests/medium_test_set/10.gr
similarity index 100%
rename from src/githubtests/medium_test_set/instances/10.gr
rename to src/githubtests/medium_test_set/10.gr
diff --git a/src/githubtests/medium_test_set/solutions/10.sol b/src/githubtests/medium_test_set/10.sol
similarity index 100%
rename from src/githubtests/medium_test_set/solutions/10.sol
rename to src/githubtests/medium_test_set/10.sol
diff --git a/src/githubtests/medium_test_set/instances/11.gr b/src/githubtests/medium_test_set/11.gr
similarity index 100%
rename from src/githubtests/medium_test_set/instances/11.gr
rename to src/githubtests/medium_test_set/11.gr
diff --git a/src/githubtests/medium_test_set/solutions/11.sol b/src/githubtests/medium_test_set/11.sol
similarity index 100%
rename from src/githubtests/medium_test_set/solutions/11.sol
rename to src/githubtests/medium_test_set/11.sol
diff --git a/src/mytests/solutions/12.sol b/src/mytests/solutions/12.sol
new file mode 100644
index 0000000..515e761
--- /dev/null
+++ b/src/mytests/solutions/12.sol
@@ -0,0 +1,124 @@
+171
+147
+177
+126
+114
+198
+204
+151
+145
+167
+220
+219
+173
+158
+112
+228
+125
+191
+178
+143
+218
+196
+154
+117
+115
+166
+118
+183
+136
+188
+165
+201
+161
+123
+130
+200
+210
+203
+172
+144
+142
+187
+153
+192
+190
+113
+160
+127
+222
+163
+230
+133
+181
+157
+225
+212
+174
+169
+131
+120
+109
+207
+229
+179
+134
+175
+141
+155
+122
+231
+211
+186
+223
+189
+184
+221
+206
+193
+168
+110
+137
+209
+138
+124
+176
+217
+164
+156
+159
+150
+139
+197
+132
+111
+227
+180
+148
+119
+135
+152
+224
+194
+116
+140
+216
+185
+199
+214
+208
+149
+205
+129
+170
+146
+121
+195
+232
+213
+182
+215
+202
+226
+128
+162
diff --git a/src/mytests/solutions/falsch12.sol b/src/mytests/solutions/falsch12.sol
new file mode 100644
index 0000000..c3737b2
--- /dev/null
+++ b/src/mytests/solutions/falsch12.sol
@@ -0,0 +1,3 @@
+[17, 18, 19, 20, 11, 12, 13, 14]
+[16, 15]
+[17, 18, 19, 20, 11, 12, 13, 14, 16, 15]
diff --git a/src/mytests/solutions/korrekt12.sol b/src/mytests/solutions/korrekt12.sol
new file mode 100644
index 0000000..28a603a
--- /dev/null
+++ b/src/mytests/solutions/korrekt12.sol
@@ -0,0 +1,3 @@
+1: [147, 171, 126, 177, 114, 198, 151, 204, 145, 167, 112, 158, 173, 219, 220, 218, 115, 117, 154, 196, 118, 166, 210, 142, 144, 172, 203, 113, 190, 192, 127, 160, 163, 222, 225, 168, 193, 206, 221, 110, 137, 209, 138, 124, 119, 148, 180, 227, 135, 152, 185, 216, 149, 208, 214, 121, 146, 170, 213, 232, 182, 202, 215, 128, 162]
+Rest: [171, 147, 177, 126, 114, 198, 204, 151, 145, 167, 220, 219, 173, 158, 112, 136, 125, 191, 143, 218, 196, 154, 117, 115, 166, 118, 228, 183, 188, 178, 165, 201, 161, 123, 130, 200, 210, 203, 172, 144, 142, 187, 181, 153, 192, 190, 113, 160, 127, 222, 163, 230, 133, 157, 225, 212, 174, 169, 131, 120, 109, 207, 229, 179, 141, 155, 134, 175, 122, 231, 211, 186, 223, 189, 184, 221, 206, 193, 168, 110, 137, 209, 138, 124, 176, 217, 164, 156, 159, 150, 139, 132, 111, 227, 180, 148, 119, 135, 152, 224, 194, 205, 226, 197, 195, 116, 140, 216, 185, 199, 214, 208, 149, 129, 170, 146, 121, 232, 213, 182, 215, 202, 128, 162]
+Zusammen: [171, 147, 177, 126, 114, 198, 204, 151, 145, 167, 220, 219, 173, 158, 112, 136, 125, 191, 143, 218, 196, 154, 117, 115, 166, 118, 228, 183, 188, 178, 165, 201, 161, 123, 130, 200, 210, 203, 172, 144, 142, 187, 181, 153, 192, 190, 113, 160, 127, 222, 163, 230, 133, 157, 225, 212, 174, 169, 131, 120, 109, 207, 229, 179, 141, 155, 134, 175, 122, 231, 211, 186, 223, 189, 184, 221, 206, 193, 168, 110, 137, 209, 138, 124, 176, 217, 164, 156, 159, 150, 139, 132, 111, 227, 180, 148, 119, 135, 152, 224, 194, 205, 226, 197, 195, 116, 140, 216, 185, 199, 214, 208, 149, 129, 170, 146, 121, 232, 213, 182, 215, 202, 128, 162]
diff --git a/src/optimiert_mit_besserer_zykel_betrachtung_und_cache.log b/src/optimiert_mit_besserer_zykel_betrachtung_und_cache.log
new file mode 100644
index 0000000..4625f15
--- /dev/null
+++ b/src/optimiert_mit_besserer_zykel_betrachtung_und_cache.log
@@ -0,0 +1,14 @@
+2024-06-13 13:58:20,155 - Prozess f�r githubtests/tiny_test_set/instances/complete_4_5.gr gestartet
+2024-06-13 13:58:20,155 - Die Ausgabedatei wird mytests/solutions\complete_4_5.sol sein
+2024-06-13 13:58:20,160 - Gr��en der Partitionen: A=4, B=5
+2024-06-13 13:58:20,160 - 20 Kanten geladen.
+2024-06-13 13:58:20,162 - x, y und c geladen.
+2024-06-13 13:58:20,162 - Zielfunktion aufgestellt.
+2024-06-13 13:58:20,163 - Crossing Constraints aufgestellt.
+2024-06-13 13:58:20,190 - Status der L�sung: Optimal
+2024-06-13 13:58:20,191 - Alle gefundenen Zyklen: 0 St�ck.
+2024-06-13 13:58:20,200 - Optimale L�sung gefunden. Ergebnisse werden gespeichert.
+2024-06-13 13:58:20,201 - Ergebnisse in mytests/solutions\complete_4_5.sol gespeichert
+2024-06-13 13:58:20,201 - Crossings: 60, in mytests/crossings\complete_4_5.cros gespeichert
+2024-06-13 13:58:20,201 - Verstrichene Zeit: 0:00:00.046 (h:m:s.ms) 
+
diff --git a/src/solver2.bat b/src/solver2.bat
new file mode 100644
index 0000000..58afd78
--- /dev/null
+++ b/src/solver2.bat
@@ -0,0 +1,3 @@
+@echo off
+python solver_knoten_grad_1_einsortieren_opt2.py %*
+
diff --git a/src/solver_knoten_grad_1_2_try.py b/src/solver_knoten_grad_1_2_try.py
new file mode 100644
index 0000000..2c1df8d
--- /dev/null
+++ b/src/solver_knoten_grad_1_2_try.py
@@ -0,0 +1,249 @@
+import math
+import sys
+import logging
+from pulp import *
+from collections import defaultdict, deque
+
+def count_crossings_via_variables(c_vars):
+    crossings = 0
+    for c_var in c_vars.values():
+        if c_var.varValue == 1:
+            crossings += 1
+    return crossings
+
+def prioritize_cycles(cycles):
+    cycles.sort(key=len, reverse=False)
+    return cycles
+
+def detect_cycle(graph, node, visited, rec_stack, path, cycles):
+    visited[node] = True
+    rec_stack[node] = True
+    path.append(node)
+
+    for neighbor in graph[node]:
+        if not visited[neighbor]:
+            detect_cycle(graph, neighbor, visited, rec_stack, path, cycles)
+        elif rec_stack[neighbor]:
+            cycle_start = path.index(neighbor)
+            cycles.append(path[cycle_start:].copy())
+
+    rec_stack[node] = False
+    path.pop()
+
+def find_all_cycles(graph, nodes):
+    visited = {node: False for node in nodes}
+    rec_stack = {node: False for node in nodes}
+    path = []
+    cycles = []
+
+    for node in nodes:
+        if not visited[node]:
+            detect_cycle(graph, node, visited, rec_stack, path, cycles)
+
+    unique_cycles = []
+    seen = set()
+    for cycle in cycles:
+        cycle_tuple = tuple(sorted(cycle))
+        if cycle_tuple not in seen:
+            seen.add(cycle_tuple)
+            unique_cycles.append(cycle)
+
+    return unique_cycles
+
+def add_cycle_constraints(prob, y, cycles, added_constraints):
+    for cycle in cycles:
+        cycle = list(dict.fromkeys(cycle))
+        for i in range(len(cycle)):
+            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:
+                    prob += y[(cycle[i], cycle[j])] + y[(cycle[j], cycle[i])] == 1
+                    added_constraints.add(constraint_1)
+                    
+                for k in range(j + 1, len(cycle)):
+                    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:
+                            prob += 0 <= y[(cycle[i], cycle[j])] + y[(cycle[j], cycle[k])] <= 1 + y[(cycle[i], cycle[k])]
+                            added_constraints.add(constraint_2)
+
+def calculate_crossings(nodes, neighbors_a):
+    pos = {node: idx for idx, node in enumerate(nodes)}
+    crossings = 0
+    for i, u in enumerate(nodes):
+        for j in range(i + 1, len(nodes)):
+            v = nodes[j]
+            for a in neighbors_a[u]:
+                for b in neighbors_a[v]:
+                    if (a < b and pos[u] > pos[v]) or (a > b and pos[u] < pos[v]):
+                        crossings += 1
+    return crossings
+
+def insert_node_with_min_crossings(sorted_nodes, degree_one_node, neighbors_a, start_index):
+    min_crossings = float('inf')
+    min_index = start_index
+    for i in range(start_index, len(sorted_nodes) + 1):
+        new_nodes = sorted_nodes[:i] + [degree_one_node] + sorted_nodes[i:]
+        new_crossings = calculate_crossings(new_nodes, neighbors_a)
+        #print(f"{i} = {new_crossings} crossings")
+        if new_crossings < min_crossings:
+            min_crossings = new_crossings
+            min_index = i
+    return sorted_nodes[:min_index] + [degree_one_node] + sorted_nodes[min_index:], min_index
+
+
+def solve_bipartite_minimization(input_lines):
+    edges = []
+    degrees_b = defaultdict(int)
+    neighbors_a = defaultdict(list)
+    
+    for line in input_lines:
+        if line.startswith('c'):
+            continue
+        elif line.startswith('p'):
+            parts = line.split()
+            n0 = int(parts[2])
+            n1 = int(parts[3])
+        else:
+            x, y = map(int, line.split())
+            edges.append((x, y))
+            degrees_b[y] += 1
+            neighbors_a[y].append(x)
+    #print(edges)
+    #print(neighbors_a)
+
+    # Identify and sort nodes in partition B with degree 1
+    degree_one_nodes = [node for node, degree in degrees_b.items() if degree == 1]
+    #print(degree_one_nodes)
+    degree_one_nodes.sort(key=lambda b: neighbors_a[b][0])
+    #print(degree_one_nodes)
+
+    degree_two_nodes = [node for node, degree in degrees_b.items() if degree == 2]
+
+    # Remove degree one nodes from nodes list
+    all_nodes = set(range(n0 + 1, n0 + n1 + 1))
+    remaining_nodes = [node for node in all_nodes if node not in degree_one_nodes]
+    if len(remaining_nodes)==0:
+        return degree_one_nodes
+    
+    remaining_nodes = [node for node in all_nodes if node not in degree_one_nodes and node not in degree_two_nodes]
+
+    # Remove edges that involve degree one nodes
+    #filtered_edges = [(i, j) for (i, j) in edges if j not in degree_one_nodes]
+    filtered_edges = [(i, j) for (i, j) in edges if j not in degree_one_nodes and j not in degree_two_nodes]
+
+    # Handle degree two nodes
+    for node in degree_two_nodes:
+        neighbors = neighbors_a[node]
+        if len(neighbors) == 2:
+            a, b = neighbors
+            if (a, b) not in filtered_edges and (b, a) not in filtered_edges:
+                filtered_edges.append((a, b))
+
+    # ILP for remaining nodes
+    prob = LpProblem("Minimize_Crossings", LpMinimize)
+    y = {(i, j): LpVariable(f"y_{i}_{j}", 0, 1, cat='Binary') for i in remaining_nodes for j in remaining_nodes if i != j}
+    c = {(i, j, k, l): LpVariable(f"c_{i}_{j}_{k}_{l}", 0, 1, cat='Binary') for (i, j) in filtered_edges for (k, l) in filtered_edges}
+
+    prob += lpSum(c.values())
+
+    for (i, j) in filtered_edges:
+            for (k, l) in filtered_edges:
+                    if k > i:
+                        if j > l and (l, j) in y:
+                            prob += c[(i, j, k, l)] == y[(l, j)]
+                        elif l > j and (j, l) in y:
+                            prob += c[(i, j, k, l)] == 1 - y[(j, l)]
+
+    added_constraints = set()
+    max_cycle_length = 9
+
+    while True:
+        prob.solve(PULP_CBC_CMD(msg=0))
+
+        if prob.status != LpStatusOptimal:
+            break
+
+        graph = defaultdict(list)
+        in_degree = defaultdict(int)
+        for i in remaining_nodes:
+            for j in remaining_nodes:
+                if i != j:
+                    y_ij = y.get((i, j))
+                    if y_ij is not None:
+                        if y_ij.varValue == 1:
+                            graph[i].append(j)
+                            in_degree[j] += 1
+                        elif y_ij.varValue == 0:
+                            graph[j].append(i)
+                            in_degree[i] += 1
+
+        all_cycles = find_all_cycles(graph, remaining_nodes)
+        
+        if not all_cycles:
+            break
+
+        prioritized_cycles = prioritize_cycles(all_cycles)
+
+        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)
+        cycles_to_process = prioritized_cycles[:k]
+        add_cycle_constraints(prob, y, cycles_to_process, added_constraints)
+
+    # Collect the topologically sorted order of the remaining nodes
+    if prob.status == LpStatusOptimal:
+        zero_in_degree_queue = deque([i for i in remaining_nodes if in_degree[i] == 0])
+        sorted_remaining_nodes = []
+        while zero_in_degree_queue:
+            node = zero_in_degree_queue.popleft()
+            sorted_remaining_nodes.append(node)
+            for neighbor in graph[node]:
+                in_degree[neighbor] -= 1
+                if in_degree[neighbor] == 0:
+                    zero_in_degree_queue.append(neighbor)
+        #print(neighbors_a)
+        #print("degree_one: ", degree_one_nodes)
+        #print("remaining: ", sorted_remaining_nodes)
+        
+        # Insert degree one nodes into sorted_remaining_nodes with minimal crossings
+        # Handle degree two nodes
+        for node in degree_two_nodes:
+            neighbors = neighbors_a[node]
+            if len(neighbors) == 2:
+                a, b = neighbors
+                if (a, b) not in filtered_edges and (b, a) not in filtered_edges:
+                    filtered_edges.append((a, b))
+
+        start_index = 0
+
+        for degree_one_node in degree_one_nodes:
+            sorted_remaining_nodes, start_index = insert_node_with_min_crossings(sorted_remaining_nodes, degree_one_node, neighbors_a, start_index)
+
+        # Insert degree two nodes back into the final order
+        for degree_two_node in degree_two_nodes:
+            neighbors = neighbors_a[degree_two_node]
+            if len(neighbors) == 2:
+                a, b = neighbors
+                if a in sorted_remaining_nodes and b in sorted_remaining_nodes:
+                    pos_a = sorted_remaining_nodes.index(a)
+                    pos_b = sorted_remaining_nodes.index(b)
+                    insert_pos = (pos_a + pos_b) // 2
+                    sorted_remaining_nodes = sorted_remaining_nodes[:insert_pos] + [degree_two_node] + sorted_remaining_nodes[insert_pos:]
+
+        return sorted_remaining_nodes
+    else:
+        return None
+
+def main():
+    input_lines = sys.stdin.read().strip().split('\n')
+    result = solve_bipartite_minimization(input_lines)
+    
+    if result:
+        print("\n".join(map(str, result)))
+    else:
+        print("No result")
+        
+if __name__ == "__main__":
+    main()
diff --git a/src/solver_knoten_grad_1_einsortieren_opt.py b/src/solver_knoten_grad_1_einsortieren_opt.py
index d674171..962c0e8 100644
--- a/src/solver_knoten_grad_1_einsortieren_opt.py
+++ b/src/solver_knoten_grad_1_einsortieren_opt.py
@@ -85,13 +85,15 @@ def insert_node_with_min_crossings(sorted_nodes, degree_one_node, neighbors_a, s
     for i in range(start_index, len(sorted_nodes) + 1):
         new_nodes = sorted_nodes[:i] + [degree_one_node] + sorted_nodes[i:]
         new_crossings = calculate_crossings(new_nodes, neighbors_a)
+        #print(f"{i} = {new_crossings} crossings")
         if new_crossings < min_crossings:
             min_crossings = new_crossings
             min_index = i
     return sorted_nodes[:min_index] + [degree_one_node] + sorted_nodes[min_index:], min_index
 
-
 def solve_bipartite_minimization(input_lines):
+    base_name = os.path.basename('korrekt12.sol')
+    output_file = os.path.join('mytests/solutions', base_name)
     edges = []
     degrees_b = defaultdict(int)
     neighbors_a = defaultdict(list)
@@ -108,18 +110,16 @@ def solve_bipartite_minimization(input_lines):
             edges.append((x, y))
             degrees_b[y] += 1
             neighbors_a[y].append(x)
-    #print(edges)
-    #print(neighbors_a)
+
     # Identify and sort nodes in partition B with degree 1
     degree_one_nodes = [node for node, degree in degrees_b.items() if degree == 1]
-    #print(degree_one_nodes)
     degree_one_nodes.sort(key=lambda b: neighbors_a[b][0])
-    #print(degree_one_nodes)
+
     # Remove degree one nodes from nodes list
     all_nodes = set(range(n0 + 1, n0 + n1 + 1))
     remaining_nodes = [node for node in all_nodes if node not in degree_one_nodes]
     
-    if len(remaining_nodes)==0:
+    if len(remaining_nodes) == 0:
         return degree_one_nodes
 
     # Remove edges that involve degree one nodes
@@ -133,16 +133,16 @@ def solve_bipartite_minimization(input_lines):
     prob += lpSum(c.values())
 
     for (i, j) in filtered_edges:
-            for (k, l) in filtered_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)] == 1 - y[(j, l)]
+        for (k, l) in filtered_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)] == 1 - y[(j, l)]
 
     added_constraints = set()
     max_cycle_length = 9
-
+    iteration = 1
     while True:
         prob.solve(PULP_CBC_CMD(msg=0))
 
@@ -164,18 +164,19 @@ def solve_bipartite_minimization(input_lines):
                             in_degree[i] += 1
 
         all_cycles = find_all_cycles(graph, remaining_nodes)
-        
+        #print("Zykel_a",iteration," ",len(all_cycles))
         if not all_cycles:
             break
 
         prioritized_cycles = prioritize_cycles(all_cycles)
 
         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) / 2)), len(prioritized_cycles))
         else:
             k = len(prioritized_cycles)
         cycles_to_process = prioritized_cycles[:k]
         add_cycle_constraints(prob, y, cycles_to_process, added_constraints)
+        iteration += 1
 
     # Collect the topologically sorted order of the remaining nodes
     if prob.status == LpStatusOptimal:
@@ -188,23 +189,43 @@ def solve_bipartite_minimization(input_lines):
                 in_degree[neighbor] -= 1
                 if in_degree[neighbor] == 0:
                     zero_in_degree_queue.append(neighbor)
-        #print(neighbors_a)
-        #print("degree_one: ", degree_one_nodes)
-        #print("remaining: ", sorted_remaining_nodes)
-        
+
+        # Combine all nodes
+        combined_nodes = sorted_remaining_nodes + degree_one_nodes
+
         # Insert degree one nodes into sorted_remaining_nodes with minimal crossings
         start_index = 0
         for degree_one_node in degree_one_nodes:
             sorted_remaining_nodes, start_index = insert_node_with_min_crossings(sorted_remaining_nodes, degree_one_node, neighbors_a, start_index)
 
-        return sorted_remaining_nodes
+        os.makedirs(os.path.dirname(output_file), exist_ok=True)
+        with open(output_file, 'w') as f:
+            f.write(f"1: {degree_one_nodes}\n")
+            f.write(f"Rest: {sorted_remaining_nodes}\n")
+            f.write(f"Zusammen: {sorted_remaining_nodes}\n")
+
+        # Optimize the order of remaining nodes using heuristic method
+        final_sorted_nodes = optimize_remaining_nodes(sorted_remaining_nodes, neighbors_a)
+
+        return final_sorted_nodes
     else:
         return None
 
+def optimize_remaining_nodes(sorted_nodes, neighbors_a):
+    improvement = True
+    while improvement:
+        improvement = False
+        for i in range(len(sorted_nodes) - 1):
+            for j in range(i + 1, len(sorted_nodes)):
+                new_nodes = sorted_nodes[:i] + [sorted_nodes[j]] + sorted_nodes[i+1:j] + [sorted_nodes[i]] + sorted_nodes[j+1:]
+                if calculate_crossings(new_nodes, neighbors_a) < calculate_crossings(sorted_nodes, neighbors_a):
+                    sorted_nodes = new_nodes
+                    improvement = True
+    return sorted_nodes
+
 def main():
     input_lines = sys.stdin.read().strip().split('\n')
     result = solve_bipartite_minimization(input_lines)
-    
     if result:
         print("\n".join(map(str, result)))
     else:
diff --git a/src/solver_knoten_grad_1_einsortieren_opt2.py b/src/solver_knoten_grad_1_einsortieren_opt2.py
new file mode 100644
index 0000000..4de13a9
--- /dev/null
+++ b/src/solver_knoten_grad_1_einsortieren_opt2.py
@@ -0,0 +1,300 @@
+import math
+import sys
+import logging
+from pulp import *
+from collections import defaultdict, deque
+
+def count_crossings_via_variables(c_vars):
+    crossings = 0
+    for c_var in c_vars.values():
+        if c_var.varValue == 1:
+            crossings += 1
+    return crossings
+
+def prioritize_cycles(cycles):
+    cycles.sort(key=len, reverse=False)
+    return cycles
+
+def detect_cycle(graph, node, visited, rec_stack, path, cycles):
+    visited[node] = True
+    rec_stack[node] = True
+    path.append(node)
+
+    for neighbor in graph[node]:
+        if not visited[neighbor]:
+            detect_cycle(graph, neighbor, visited, rec_stack, path, cycles)
+        elif rec_stack[neighbor]:
+            cycle_start = path.index(neighbor)
+            cycles.append(path[cycle_start:].copy())
+
+    rec_stack[node] = False
+    path.pop()
+
+def find_all_cycles(graph, nodes):
+    visited = {node: False for node in nodes}
+    rec_stack = {node: False for node in nodes}
+    path = []
+    cycles = []
+
+    for node in nodes:
+        if not visited[node]:
+            detect_cycle(graph, node, visited, rec_stack, path, cycles)
+
+    unique_cycles = []
+    seen = set()
+    for cycle in cycles:
+        cycle_tuple = tuple(sorted(cycle))
+        if cycle_tuple not in seen:
+            seen.add(cycle_tuple)
+            unique_cycles.append(cycle)
+
+    return unique_cycles
+
+def add_cycle_constraints(prob, y, cycles, added_constraints):
+    for cycle in cycles:
+        cycle = list(dict.fromkeys(cycle))
+        for i in range(len(cycle)):
+            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:
+                    prob += y[(cycle[i], cycle[j])] + y[(cycle[j], cycle[i])] == 1
+                    added_constraints.add(constraint_1)
+                    
+                for k in range(j + 1, len(cycle)):
+                    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:
+                            prob += 0 <= y[(cycle[i], cycle[j])] + y[(cycle[j], cycle[k])] <= 1 + y[(cycle[i], cycle[k])]
+                            added_constraints.add(constraint_2)
+
+def calculate_crossings(nodes, neighbors_a):
+    pos = {node: idx for idx, node in enumerate(nodes)}
+    crossings = 0
+    for i, u in enumerate(nodes):
+        for j in range(i + 1, len(nodes)):
+            v = nodes[j]
+            for a in neighbors_a[u]:
+                for b in neighbors_a[v]:
+                    if (a < b and pos[u] > pos[v]) or (a > b and pos[u] < pos[v]):
+                        crossings += 1
+    return crossings
+
+def insert_node_with_min_crossings(sorted_nodes, degree_one_node, neighbors_a, start_index):
+    min_crossings = float('inf')
+    min_index = start_index
+    for i in range(start_index, len(sorted_nodes) + 1):
+        new_nodes = sorted_nodes[:i] + [degree_one_node] + sorted_nodes[i:]
+        new_crossings = calculate_crossings(new_nodes, neighbors_a)
+        #print(f"{i} = {new_crossings} crossings")
+        if new_crossings < min_crossings:
+            min_crossings = new_crossings
+            min_index = i
+    return sorted_nodes[:min_index] + [degree_one_node] + sorted_nodes[min_index:], min_index
+
+
+def solve_bipartite_minimization(input_lines):
+    base_name = os.path.basename('korrekt12.sol')
+    output_file = os.path.join('mytests/solutions', base_name)
+    edges = []
+    degrees_b = defaultdict(int)
+    neighbors_a = defaultdict(list)
+    
+    for line in input_lines:
+        if line.startswith('c'):
+            continue
+        elif line.startswith('p'):
+            parts = line.split()
+            n0 = int(parts[2])
+            n1 = int(parts[3])
+        else:
+            x, y = map(int, line.split())
+            edges.append((x, y))
+            degrees_b[y] += 1
+            neighbors_a[y].append(x)
+    #print(edges)
+    #print(neighbors_a)
+    # Identify and sort nodes in partition B with degree 1
+    degree_one_nodes = [node for node, degree in degrees_b.items() if degree == 1]
+    #print(degree_one_nodes)
+    degree_one_nodes.sort(key=lambda b: neighbors_a[b][0])
+    #print(degree_one_nodes)
+    # Remove degree one nodes from nodes list
+    all_nodes = set(range(n0 + 1, n0 + n1 + 1))
+    remaining_nodes = [node for node in all_nodes if node not in degree_one_nodes]
+    #print(remaining_nodes)
+    if len(remaining_nodes)==0:
+        return degree_one_nodes
+
+    # Remove edges that involve degree one nodes
+    filtered_edges = [(i, j) for (i, j) in edges if j not in degree_one_nodes]
+
+    # ILP for remaining nodes
+    prob = LpProblem("Minimize_Crossings", LpMinimize)
+    y = {(i, j): LpVariable(f"y_{i}_{j}", 0, 1, cat='Binary') for i in remaining_nodes for j in remaining_nodes if i != j}
+    c = {(i, j, k, l): LpVariable(f"c_{i}_{j}_{k}_{l}", 0, 1, cat='Binary') for (i, j) in filtered_edges for (k, l) in filtered_edges}
+
+    prob += lpSum(c.values())
+
+    for (i, j) in filtered_edges:
+            for (k, l) in filtered_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)] == 1 - y[(j, l)]
+
+    added_constraints = set()
+    max_cycle_length = 9
+    iteration = 1
+    while True:
+        prob.solve(PULP_CBC_CMD(msg=0))
+
+        if prob.status != LpStatusOptimal:
+            break
+
+        graph = defaultdict(list)
+        in_degree = defaultdict(int)
+        for i in remaining_nodes:
+            for j in remaining_nodes:
+                if i != j:
+                    y_ij = y.get((i, j))
+                    if y_ij is not None:
+                        if y_ij.varValue == 1:
+                            graph[i].append(j)
+                            in_degree[j] += 1
+                        elif y_ij.varValue == 0:
+                            graph[j].append(i)
+                            in_degree[i] += 1
+
+        all_cycles = find_all_cycles(graph, remaining_nodes)
+        #print("Zykel_a",iteration," ",len(all_cycles))
+        if not all_cycles:
+            break
+
+        prioritized_cycles = prioritize_cycles(all_cycles)
+
+        if len(prioritized_cycles[-1]) > max_cycle_length:
+            k = min(int(math.floor(len(prioritized_cycles) / 2)), len(prioritized_cycles))
+        else:
+            k = len(prioritized_cycles)
+        cycles_to_process = prioritized_cycles[:k]
+        add_cycle_constraints(prob, y, cycles_to_process, added_constraints)
+        iteration += 1
+
+    # Collect the topologically sorted order of the remaining nodes
+    if prob.status == LpStatusOptimal:
+        zero_in_degree_queue = deque([i for i in remaining_nodes if in_degree[i] == 0])
+        sorted_remaining_nodes = []
+        while zero_in_degree_queue:
+            node = zero_in_degree_queue.popleft()
+            sorted_remaining_nodes.append(node)
+            for neighbor in graph[node]:
+                in_degree[neighbor] -= 1
+                if in_degree[neighbor] == 0:
+                    zero_in_degree_queue.append(neighbor)
+        #print(neighbors_a)
+        #print("degree_one: ", degree_one_nodes)
+        #print("remaining: ", sorted_remaining_nodes)
+        # Combine all nodes
+        combined_nodes = sorted_remaining_nodes + degree_one_nodes
+        # Insert degree one nodes into sorted_remaining_nodes with minimal crossings
+        # Use the result as a warm start for a new ILP
+        final_prob = LpProblem("Minimize_Final_Crossings", LpMinimize)
+        final_y = {(i, j): LpVariable(f"final_y_{i}_{j}", 0, 1, cat='Binary') for i in combined_nodes for j in combined_nodes if i != j}
+        final_c = {(i, j, k, l): LpVariable(f"final_c_{i}_{j}_{k}_{l}", 0, 1, cat='Binary') for (i, j) in edges for (k, l) in edges}
+
+        final_prob += lpSum(final_c.values())
+
+        for (i, j) in edges:
+            for (k, l) in edges:
+                if k > i:
+                    if j > l:
+                        final_prob += final_c[(i, j, k, l)] == final_y[(l, j)]
+                    elif l > j:
+                        final_prob += final_c[(i, j, k, l)] == 1 - final_y[(j, l)]
+        """
+        # Warm start using the sorted_remaining_nodes
+        for i in range(len(sorted_remaining_nodes) - 1):
+            for j in range(i + 1, len(sorted_remaining_nodes) - 1):
+                final_y[(sorted_remaining_nodes[i], sorted_remaining_nodes[j])].setInitialValue(1)
+                final_y[(sorted_remaining_nodes[j], sorted_remaining_nodes[i])].setInitialValue(0)
+        """
+        for i in range(len(sorted_remaining_nodes) - 1):
+            final_y[(sorted_remaining_nodes[i], sorted_remaining_nodes[i + 1])].setInitialValue(1)
+        for i in range(len(degree_one_nodes) - 1):
+            final_y[(degree_one_nodes[i], degree_one_nodes[i + 1])].setInitialValue(1)
+            
+        added_constraints = set()
+        max_cycle_length = 9
+        iteration = 1
+        while True:
+            final_prob.solve(PULP_CBC_CMD(msg=0, warmStart=True))
+
+            if final_prob.status != LpStatusOptimal:
+                break
+
+            graph = defaultdict(list)
+            in_degree = defaultdict(int)
+            for i in all_nodes:
+                for j in all_nodes:
+                    if i != j:
+                        y_ij = final_y.get((i, j))
+                        if y_ij is not None:
+                            if y_ij.varValue == 1:
+                                graph[i].append(j)
+                                in_degree[j] += 1
+                            elif y_ij.varValue == 0:
+                                graph[j].append(i)
+                                in_degree[i] += 1
+
+            all_cycles = find_all_cycles(graph, all_nodes)
+            #print("Zykel_b",iteration," ",len(all_cycles))
+            if not all_cycles:
+                break
+
+            prioritized_cycles = prioritize_cycles(all_cycles)
+
+            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)
+            cycles_to_process = prioritized_cycles[:k]
+            add_cycle_constraints(final_prob, final_y, cycles_to_process, added_constraints)
+            iteration += 1
+
+        # Collect the topologically sorted order of the remaining nodes
+        if final_prob.status == LpStatusOptimal:
+
+            zero_in_degree_queue = deque([i for i in all_nodes if in_degree[i] == 0])
+            final_sorted_nodes = []
+            while zero_in_degree_queue:
+                node = zero_in_degree_queue.popleft()
+                final_sorted_nodes.append(node)
+                for neighbor in graph[node]:
+                    in_degree[neighbor] -= 1
+                    if in_degree[neighbor] == 0:
+                        zero_in_degree_queue.append(neighbor)
+
+            os.makedirs(os.path.dirname(output_file), exist_ok=True)
+            with open(output_file, 'w') as f:
+                f.write(f"{degree_one_nodes}\n")
+                f.write(f"{sorted_remaining_nodes}\n")
+                f.write(f"{final_sorted_nodes}\n")
+
+            return final_sorted_nodes
+        
+        else:
+            return None
+    else:
+        return None
+
+def main():
+    input_lines = sys.stdin.read().strip().split('\n')
+    result = solve_bipartite_minimization(input_lines)
+    if result:
+        print("\n".join(map(str, result)))
+    else:
+        print("No result")
+        
+if __name__ == "__main__":
+    main()
diff --git a/src/solver_opt.py b/src/solver_opt.py
index c858917..8d745f2 100644
--- a/src/solver_opt.py
+++ b/src/solver_opt.py
@@ -96,7 +96,7 @@ def solve_bipartite_minimization(input_lines):
                 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
@@ -153,7 +153,6 @@ 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
-- 
GitLab