100 lines
3.9 KiB
Python
100 lines
3.9 KiB
Python
|
|
#!/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()
|