#!/usr/bin/env python3 # erzeuge_spaltengenerierung_flow.py """ Erzeugt das Regelkreis-Diagramm zum Dekompositions-Kapitel: bilder_04/kap_dekomposition_regelkreis.svg Spaltengenerierung ist kein Algorithmus, den man am Stueck liest, sondern ein Kreislauf zwischen zwei Problemen, die einander abwechselnd Zahlen zuwerfen: Master-LP -- Dualwerte pi_i --> Pricing (Rucksack) Master-LP <-- neue Spalte a -- Pricing Das Bild macht daraus einen Kreis mit der Abbruchbedingung als Ausgang. Es ist bewusst schematisch: Zahlen stehen keine darin, denn die Schleife gilt fuer jede Instanz gleichermassen. Was sie zeigt, ist die Richtung der Information - und dass das Verfahren erst endet, wenn das Pricing NICHTS mehr findet. Gezeichnet mit matplotlib.patches statt Graphviz: Das Bild hat fuenf Kaesten und vier Pfeile: dafuer eine weitere Abhaengigkeit einzufuehren, waere unverhaeltnismaessig - und die Positionen sollen zu den uebrigen Diagrammen des Buchs passen, nicht zu Graphviz' Layoutgeschmack. Aufruf (aus dem Repository-Wurzelverzeichnis): python3 bilder_04/erzeuge_spaltengenerierung_flow.py Benoetigt: matplotlib """ from __future__ import annotations import os import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from stil_04 import FARBEN, speichere # noqa: E402 import matplotlib.pyplot as plt # noqa: E402 from matplotlib.patches import FancyArrowPatch, FancyBboxPatch # noqa: E402 def kasten(achse, x, y, breite, hoehe, titel, zeilen, farbe, fuellung): achse.add_patch(FancyBboxPatch( (x, y), breite, hoehe, boxstyle="round,pad=0,rounding_size=0.09", facecolor=fuellung, edgecolor=farbe, linewidth=1.8, zorder=2)) achse.text(x + breite / 2, y + hoehe - 0.20, titel, ha="center", va="center", fontsize=10, fontweight="bold", color=farbe, zorder=3) achse.text(x + breite / 2, y + hoehe / 2 - 0.16, zeilen, ha="center", va="center", fontsize=8.8, color=FARBEN["text"], zorder=3) def pfeil(achse, von, nach, text, farbe, bogen=0.0, versatz=(0, 0)): achse.add_patch(FancyArrowPatch( von, nach, arrowstyle="-|>", mutation_scale=16, linewidth=1.8, color=farbe, connectionstyle=f"arc3,rad={bogen}", zorder=4, shrinkA=2, shrinkB=2)) mitte = ((von[0] + nach[0]) / 2 + versatz[0], (von[1] + nach[1]) / 2 + versatz[1]) achse.text(*mitte, text, ha="center", va="center", fontsize=9, color=farbe, fontweight="bold", zorder=5) def zeichne() -> None: figur, achse = plt.subplots(figsize=(8.6, 4.3)) achse.set_xlim(0, 10) achse.set_ylim(0, 5) achse.axis("off") kasten(achse, 0.3, 2.5, 3.5, 1.9, "Master-LP (RMP)", "rechnet mit den bisher\nbekannten Mustern\nund liefert Dualwerte", FARBEN["haupt"], "#eef2ff") kasten(achse, 6.2, 2.5, 3.5, 1.9, "Pricing: Rucksackproblem", "sucht das EINE Muster mit\nden kleinsten reduzierten\nKosten", FARBEN["zweit"], "#ecfeff") kasten(achse, 3.1, 0.15, 3.8, 1.35, "Optimum bewiesen", "kein Muster mit negativen\nreduzierten Kosten mehr", FARBEN["gut"], "#ecfdf5") pfeil(achse, (3.9, 3.9), (6.1, 3.9), r"Dualwerte $\pi_i$", FARBEN["haupt"], bogen=-0.28, versatz=(0, 0.62)) pfeil(achse, (6.1, 3.0), (3.9, 3.0), r"neue Spalte $a$, wenn $1-\sum_i \pi_i a_i < 0$", FARBEN["zweit"], bogen=-0.28, versatz=(0, -0.72)) pfeil(achse, (7.4, 2.4), (6.0, 1.5), "sonst", FARBEN["gut"], bogen=0.25, versatz=(0.55, 0.12)) achse.text(5.0, 4.85, "Die Schleife endet nicht, wenn sie müde wird, sondern wenn das " "Pricing nichts mehr findet.", ha="center", fontsize=9.5, color=FARBEN["gedaempft"], style="italic") speichere(figur, "kap_dekomposition_regelkreis") if __name__ == "__main__": zeichne()