diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d4a7e6342aadfa4c073470fe8ecd841964757105 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +test.py +test?.py \ No newline at end of file diff --git a/README.md b/README.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7f0d7b78ed9991be9421112887b4f03ae79cf2fc 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,48 @@ +# Backend + +## Project Structure + + APPALGO-SOSE24 + Backend + │ + └── src + | + ├── main.py + │ + └── requirements.txt + +## Setup + +1. **Install Python**: go to [python.org](https://wiki.python.org/moin/BeginnersGuide/Download) + +3. **Navigate to Backend**: + + ```bash + cd appalgo-sose24/src + ``` + +4. **Create and Activate a Virtual Environment** (Optional but recommended): + + ```bash + python3 -m venv venv + ``` + In a Unix-based System run: + ```bash + source venv/bin/activate + ``` + In Windows run: + ```bash + venv\Scripts\activate + ``` + +5. **Install the Dependencies:** + + ```bash + pip install -r requirements.txt + ``` + +## Test Setup + +1. Geh sicher, dass alle Dependencies installiert sind. + +2. \ No newline at end of file diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ce6672d4d95b08d24d69752d49d76b70c93aed43 --- /dev/null +++ b/src/README.md @@ -0,0 +1,10 @@ +1. Crossing Variables C_i_j_k_l + + +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. \ No newline at end of file diff --git a/src/backupmain.py b/src/backupmain.py new file mode 100644 index 0000000000000000000000000000000000000000..635dfb35ba166ec4035a8922eff5d885b15fb1e8 --- /dev/null +++ b/src/backupmain.py @@ -0,0 +1,99 @@ +import os +import logging +# Konfiguriere Logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +from pulp import * + +def solve_bipartite_minimization(graph_file): + logging.info(f"Prozess für {graph_file} gestartet") + + # Extrahiere den Basisnamen der Eingabedatei + base_name = os.path.basename(graph_file) + # Erstelle den Ausgabepfad + output_file = os.path.join('solution_instances', base_name) + logging.info(f"Die Ausgabedatei wird {output_file} sein") + + 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]) # Anzahl der Knoten in A + n1 = int(parts[3]) # Anzahl der Knoten in B + logging.info(f"Größen der Partitionen: A={n0}, B={n1}") + else: + x, y = map(int, line.split()) + edges.append((x, y)) + logging.info(f"{len(edges)} Kanten geladen.") + + prob = LpProblem("Minimize_Crossings", LpMinimize) + + # Boolesche Variablen für relative Positionen innerhalb jeder Partition + x = {(i, j): LpVariable(f"x_{i}_{j}", 0, 1, cat='Binary') for i in range(1, n0 + 1) for j in range(1, n0 + 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} + logging.info("x und y geladen.") + + # Crossing Variables + c = {} + for (i, j) in edges: + for (k, l) in edges: + if i < j and k < l and i < k and j != l: + c[(i, j, k, l)] = LpVariable(f"c_{i}_{j}_{k}_{l}", 0, 1, cat='Binary') + logging.info("c geladen.") + # Zielfunktion, die minimiert werden soll + prob += lpSum(c.values()) + logging.info("Zielfunktion aufgestellt.") + # Crossing Constraints + for (i, j, k, l) in c: + if j < l: + prob += -c[(i, j, k, l)] <= y[(j, l)] - x[(i, k)] <= c[(i, j, k, l)] + if l > j: + prob += 1 - c[(i, j, k, l)] <= y[(l, j)] + x[(i, k)] <= 1 + c[(i, j, k, l)] + logging.info("Crossing Constraints aufgestellt.") + + # Constraints für x und y + for i in range(1, n0): + for j in range(i + 1, n0 + 1): + for k in range(j + 1, n0 + 1): + prob += 0 <= x[(i, j)] + x[(j, k)] - x[(i, k)] == 1 + logging.info("X fest Constraints aufgestellt.") + + for i in range(n0 + 1, n0 + n1): + for j in range(i + 1, n0 + n1 + 1): + for k in range(j + 1, n0 + n1 + 1): + prob += 0 <= y[(i, j)] + y[(j, k)] - y[(i, k)] <= 1 + logging.info("Y variable Constraints aufgestellt.") + + # Constraints für die relative Ordnung innerhalb der Partitionen + for (i, j) in y: + prob += y[(i, j)] + y[(j, i)] == 1 # genau einer muss wahr sein + logging.info("Relative Ordnung muss wahr sein.") + + prob.solve() + logging.info(f"Status der Lösung: {LpStatus[prob.status]}") + + # Check if a solution exists and print the results + if prob.status == LpStatusOptimal: + logging.info("Optimale Lösung gefunden. Ergebnisse werden gespeichert.") + # Berechne zuerst die Summen in einer Liste + position_sum = [(j, sum(y[(j, k)].value() for k in range(n0+1, n0+n1+1) if j != k)) for j in range(n0+1, n0+n1+1)] + + # Nutze diese Liste dann im sorted() Aufruf + sorted_b = sorted(position_sum, key=lambda x: -x[1]) + + # Stelle sicher, dass das Verzeichnis existiert + os.makedirs(os.path.dirname(output_file), exist_ok=True) + + # Speichere die sortierten Ergebnisse in der Ausgabedatei + with open(output_file, 'w') as f: + for b, _ in sorted_b: + f.write(f"{b}\n") + logging.info(f"Ergebnisse in {output_file} gespeichert") + else: + logging.warning("Keine optimale Lösung gefunden.") + + +test_file = 'test_instances/1.gr' +solve_bipartite_minimization(test_file) diff --git a/src/fortesting.py b/src/fortesting.py new file mode 100644 index 0000000000000000000000000000000000000000..6ee4ef50bb16137824b600671b193e7f93ae133e --- /dev/null +++ b/src/fortesting.py @@ -0,0 +1,95 @@ +import os +from pulp import * + +def solve_bipartite_minimization(graph_file): + + # Extrahiere den Basisnamen der Eingabedatei + base_name = os.path.basename(graph_file) + # Erstelle den Ausgabepfad + output_file = os.path.join('solution_instances', base_name) + + 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]) # Anzahl der Knoten in A + n1 = int(parts[3]) # Anzahl der Knoten in B + else: + x, y = map(int, line.split()) + edges.append((x, y)) + + prob = LpProblem("Minimize_Crossings", LpMinimize) + + # Boolesche Variablen für relative Positionen innerhalb jeder Partition + x = {(i, j): LpVariable(f"x_{i}_{j}", 0, 1, cat='Binary') for i in range(1, n0 + 1) for j in range(1, n0 + 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} + #print("x:",x) + #print("y:",y) + + # Crossing Variables + c = {} + for (i, j) in edges: + for (k, l) in edges: + if i < j and k < l and i < k and j != l: + c[(i, j, k, l)] = LpVariable(f"c_{i}_{j}_{k}_{l}", 0, 1, cat='Binary') + + # Zielfunktion, die minimiert werden soll + prob += lpSum(c.values()) + + # Crossing Constraints + for (i, j, k, l) in c: + if j < l: + prob += -c[(i, j, k, l)] <= y[(j, l)] - x[(i, k)] <= c[(i, j, k, l)] + if l > j: + prob += 1 - c[(i, j, k, l)] <= y[(l, j)] + x[(i, k)] <= 1 + c[(i, j, k, l)] + + # Constraints für x und y + for i in range(1, n0): + for j in range(i + 1, n0 + 1): + for k in range(j + 1, n0 + 1): + prob += 0 <= x[(i, j)] + x[(j, k)] - x[(i, k)] == 1 + + for i in range(n0 + 1, n0 + n1): + for j in range(i + 1, n0 + n1 + 1): + for k in range(j + 1, n0 + n1 + 1): + prob += 0 <= y[(i, j)] + y[(j, k)] - y[(i, k)] <= 1 + + # Constraints für die relative Ordnung innerhalb der Partitionen + for (i, j) in y: + prob += y[(i, j)] + y[(j, i)] == 1 # genau einer muss wahr sein + + prob.solve() + + print("Status:", LpStatus[prob.status]) + + for v in prob.variables(): + print(v.name, "=", v.varValue) + + # Check if a solution exists and print the results + if prob.status == LpStatusOptimal: + print("Optimal arrangement of B:") + + # Berechne zuerst die Summen in einer Liste + position_sum = [(j, sum(y[(j, k)].value() for k in range(n0+1, n0+n1+1) if j != k)) for j in range(n0+1, n0+n1+1)] + + # Nutze diese Liste dann im sorted() Aufruf + sorted_b = sorted(position_sum, key=lambda x: -x[1]) + + # Stelle sicher, dass das Verzeichnis existiert + os.makedirs(os.path.dirname(output_file), exist_ok=True) + + # Speichere die sortierten Ergebnisse in der Ausgabedatei + with open(output_file, 'w') as f: + for b, _ in sorted_b: + f.write(f"{b}\n") + print(f"{b}") + print(f"Results saved to {output_file}") + else: + print("No optimal solution found.") + + +test_file = 'test_instances/0.gr' +solve_bipartite_minimization(test_file) diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000000000000000000000000000000000000..ecc563ac1de900d4211212b385ce095feb39394f --- /dev/null +++ b/src/main.py @@ -0,0 +1,99 @@ +import os +import logging +# Konfiguriere Logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +from pulp import * + +def solve_bipartite_minimization(graph_file): + logging.info(f"Prozess für {graph_file} gestartet") + + # Extrahiere den Basisnamen der Eingabedatei + base_name = os.path.basename(graph_file) + # Erstelle den Ausgabepfad + output_file = os.path.join('solution_instances', base_name) + logging.info(f"Die Ausgabedatei wird {output_file} sein") + + 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]) # Anzahl der Knoten in A + n1 = int(parts[3]) # Anzahl der Knoten in B + logging.info(f"Größen der Partitionen: A={n0}, B={n1}") + else: + x, y = map(int, line.split()) + edges.append((x, y)) + logging.info(f"{len(edges)} Kanten geladen.") + + prob = LpProblem("Minimize_Crossings", LpMinimize) + + # Boolesche Variablen für relative Positionen innerhalb jeder Partition + x = {(i, j): LpVariable(f"x_{i}_{j}", 0, 1, cat='Binary') for i in range(1, n0 + 1) for j in range(1, n0 + 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} + logging.info("x und y geladen.") + + # Crossing Variables + c = {} + for (i, j) in edges: + for (k, l) in edges: + if i < j and k < l and i < k and j != l: + c[(i, j, k, l)] = LpVariable(f"c_{i}_{j}_{k}_{l}", 0, 1, cat='Binary') + logging.info("c geladen.") + # Zielfunktion, die minimiert werden soll + prob += lpSum(c.values()) + logging.info("Zielfunktion aufgestellt.") + # Crossing Constraints + for (i, j, k, l) in c: + if j < l: + prob += -c[(i, j, k, l)] <= y[(j, l)] - x[(i, k)] <= c[(i, j, k, l)] + if l > j: + prob += 1 - c[(i, j, k, l)] <= y[(l, j)] + x[(i, k)] <= 1 + c[(i, j, k, l)] + logging.info("Crossing Constraints aufgestellt.") + + # Constraints für x und y (ZU LANGE LAUFZEIT) + for i in range(1, n0): + for j in range(i + 1, n0 + 1): + for k in range(j + 1, n0 + 1): + prob += 0 <= x[(i, j)] + x[(j, k)] - x[(i, k)] == 1 + logging.info("X fest Constraints aufgestellt.") + + for i in range(n0 + 1, n0 + n1): + for j in range(i + 1, n0 + n1 + 1): + for k in range(j + 1, n0 + n1 + 1): + prob += 0 <= y[(i, j)] + y[(j, k)] - y[(i, k)] <= 1 + logging.info("Y variable Constraints aufgestellt.") + + # Constraints für die relative Ordnung innerhalb der Partitionen + for (i, j) in y: + prob += y[(i, j)] + y[(j, i)] == 1 # genau einer muss wahr sein + logging.info("Relative Ordnung muss wahr sein.") + + prob.solve() + logging.info(f"Status der Lösung: {LpStatus[prob.status]}") + + # Check if a solution exists and print the results + if prob.status == LpStatusOptimal: + logging.info("Optimale Lösung gefunden. Ergebnisse werden gespeichert.") + # Berechne zuerst die Summen in einer Liste + position_sum = [(j, sum(y[(j, k)].value() for k in range(n0+1, n0+n1+1) if j != k)) for j in range(n0+1, n0+n1+1)] + + # Nutze diese Liste dann im sorted() Aufruf + sorted_b = sorted(position_sum, key=lambda x: -x[1]) + + # Stelle sicher, dass das Verzeichnis existiert + os.makedirs(os.path.dirname(output_file), exist_ok=True) + + # Speichere die sortierten Ergebnisse in der Ausgabedatei + with open(output_file, 'w') as f: + for b, _ in sorted_b: + f.write(f"{b}\n") + logging.info(f"Ergebnisse in {output_file} gespeichert") + else: + logging.warning("Keine optimale Lösung gefunden.") + + +test_file = 'test_instances/0.gr' +solve_bipartite_minimization(test_file) diff --git a/src/requirements.txt b/src/requirements.txt index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9604acd68094003a769edd98890d88b16d19d571 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -0,0 +1,2 @@ +pulp==2.6.0 +pace2024-verifier diff --git a/src/solution_instances/0.gr b/src/solution_instances/0.gr new file mode 100644 index 0000000000000000000000000000000000000000..292ff76f03cd38c78e04979106e1fcb289a304c8 --- /dev/null +++ b/src/solution_instances/0.gr @@ -0,0 +1,3 @@ +5 +4 +6 diff --git a/src/test_instances/0.gr b/src/test_instances/0.gr new file mode 100644 index 0000000000000000000000000000000000000000..edee319e50c97e75cd21eb46ab3a9d7c8e9877b0 --- /dev/null +++ b/src/test_instances/0.gr @@ -0,0 +1,4 @@ +p ocr 3 3 3 +1 5 +2 4 +3 6 \ No newline at end of file