Version 04 als eigenes Repository
Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python"
(Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im
uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch
Version_03 (eingefroren) verwaltet und Version_04/ ignoriert.
Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit
der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen
Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist.
Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise,
325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren,
25 Notebooks, PDF mit 715 Seiten.
Zusaetzlich in diesem Commit:
* pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api,
figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt
unveraendert daneben bestehen. ortools steht in der Grundausstattung,
highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der
schlanken Installation gar nicht erst auftreten.
* Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py
importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo
steht in requirements.txt, wird aber von keinem Programm importiert,
sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in
[empfehlungen]).
* NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es
ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden
Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen.
* PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den
tatsaechlichen aus erzeuge_titelseite.py korrigiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
|
|
# erzeuge_solverwahl_diagramme.py
|
|
|
|
|
"""
|
|
|
|
|
Erzeugt die beiden Entscheidungs-Flussdiagramme "Welches Werkzeug passt zu
|
|
|
|
|
meinem Problem?" fuer die Auftakte von Teil II und Teil III:
|
|
|
|
|
|
Aufgeraeumt: 33 PNG-Zweitfassungen, Bau-Ueberbleibsel, Ausgabepfade
Drei Aufraeumarbeiten - und zwei Funde, die dabei auffielen.
1. DIE PNG-ZWEITFASSUNGEN SIND WEG. Jedes Diagramm lag doppelt vor, als SVG
und als PNG, und kein einziges src=/href= in der Website zeigte je auf ein
PNG. Der Build kopierte sie trotzdem mit: 3,3 MB im Repository plus 3,3 MB,
die bei jeder Veroeffentlichung auf den Webserver gingen. Die 15 Generatoren
schreiben jetzt nur noch SVG, die Docstrings sind mitgezogen. Vor dem
Loeschen geprueft: Jedes PNG hatte sein gleichnamiges SVG, alle 33 waren
versioniert.
FUND 1: erzeuge_kap06_gantt.py folgte als einziger Generator nicht der
Konvention - weder svg.hashsalt noch metadata={"Date": None}. Sein SVG trug
einen echten Zeitstempel und bei jedem Lauf andere clip-path-IDs, war also
nie byteidentisch reproduzierbar, obwohl CLAUDE.md genau das fuer alle
Generatoren festhaelt. Aufgefallen nur, weil nach der PNG-Umstellung 32 von
33 SVGs bitgleich blieben und eines nicht. Jetzt byteidentisch ueber zwei
Laeufe.
FUND 2: spiegle_bilder() legte leere Verzeichnisse auf dem Webserver an. Der
Dateifilter arbeitete korrekt, aber os.walk durchlief auch __pycache__/, und
os.makedirs() erzeugte es am Ziel. Die Verzeichnisliste wird jetzt vorher
gefiltert. Gegengetestet.
2. BAU-UEBERBLEIBSEL entfernt (alle ignoriert und neu erzeugbar):
svg-inkscape/, build_v04.log, .pytest_cache/, Programme/output/, vier
__pycache__/ und die Excel-Mappen. Arbeitsbaum 36 -> 33 MB, danach null
ignorierte Ueberbleibsel.
3. Excel_Bruecke.py SCHREIBT NEBEN DAS SKRIPT statt ins
Arbeitsverzeichnis. Es benutzte blanke relative Namen; wer es aus der
Repository-Wurzel startete, verstreute dort produktionsmix.xlsx und
produktionsmix_ergebnis.xlsx. Jetzt wie die vier anderen schreibenden
Programme ueber os.path.dirname(os.path.abspath(__file__)). Nachgemessen:
Lauf aus der Wurzel legt dort null Dateien ab.
Ausserdem git gc: 653 lose Objekte gepackt, .git von 71 MB auf 28 MB -
reines Repacken, kein Inhalt beruehrt.
Geprueft: alle 33 im Buch referenzierten SVGs vorhanden, in Quelle und
Website; 16 Generatoren fehlerfrei; keine fehlenden Bilder im LaTeX-Lauf;
33 pytest-Tests; PDF unveraendert 760 Seiten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 14:22:26 +02:00
|
|
|
bilder_04/teil2_solverwahl.svg deterministische Verfahren
|
|
|
|
|
bilder_04/teil3_solverwahl.svg Nichtlinearitaet und Unsicherheit
|
Version 04 als eigenes Repository
Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python"
(Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im
uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch
Version_03 (eingefroren) verwaltet und Version_04/ ignoriert.
Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit
der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen
Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist.
Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise,
325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren,
25 Notebooks, PDF mit 715 Seiten.
Zusaetzlich in diesem Commit:
* pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api,
figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt
unveraendert daneben bestehen. ortools steht in der Grundausstattung,
highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der
schlanken Installation gar nicht erst auftreten.
* Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py
importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo
steht in requirements.txt, wird aber von keinem Programm importiert,
sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in
[empfehlungen]).
* NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es
ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden
Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen.
* PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den
tatsaechlichen aus erzeuge_titelseite.py korrigiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
|
|
|
|
|
|
|
|
Beide entstehen mit Graphviz. Die Farben folgen der Buchpalette
|
|
|
|
|
(Indigo, Cyan, Violett, Gruen, Amber), die Kastenform codiert die Rolle:
|
|
|
|
|
Raute = Frage, abgerundeter Kasten = Empfehlung.
|
|
|
|
|
|
|
|
|
|
Aufruf (aus dem Repository-Wurzelverzeichnis):
|
|
|
|
|
python3 bilder_04/erzeuge_solverwahl_diagramme.py
|
|
|
|
|
|
|
|
|
|
Benoetigt: graphviz (Python-Paket) und das Kommandozeilenwerkzeug 'dot'
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
import graphviz
|
|
|
|
|
|
|
|
|
|
BASIS = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
|
BILDER = os.path.join(BASIS, "bilder_04")
|
|
|
|
|
|
|
|
|
|
INDIGO, CYAN, VIOLETT, GRUEN, AMBER = (
|
|
|
|
|
"#4338ca", "#0891b2", "#7c3aed", "#059669", "#b45309")
|
|
|
|
|
TINT = {INDIGO: "#eef2ff", CYAN: "#ecfeff", VIOLETT: "#f5f3ff",
|
|
|
|
|
GRUEN: "#ecfdf5", AMBER: "#fffbeb"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def neuer_graph(titel: str) -> graphviz.Digraph:
|
|
|
|
|
punkt = graphviz.Digraph(format="svg")
|
|
|
|
|
punkt.attr(rankdir="TB", splines="polyline", nodesep="0.35", ranksep="0.45",
|
|
|
|
|
fontname="DejaVu Sans", label=titel, labelloc="t",
|
|
|
|
|
fontsize="13", bgcolor="transparent")
|
|
|
|
|
punkt.attr("node", fontname="DejaVu Sans", fontsize="10")
|
|
|
|
|
punkt.attr("edge", fontname="DejaVu Sans", fontsize="9", color="#5c6380")
|
|
|
|
|
return punkt
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def frage(punkt: graphviz.Digraph, name: str, text: str, farbe: str = INDIGO) -> None:
|
|
|
|
|
punkt.node(name, text, shape="diamond", style="filled",
|
|
|
|
|
fillcolor=TINT[farbe], color=farbe, penwidth="1.6",
|
|
|
|
|
margin="0.08,0.02")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def empfehlung(punkt: graphviz.Digraph, name: str, text: str, farbe: str) -> None:
|
|
|
|
|
punkt.node(name, text, shape="box", style="filled,rounded",
|
|
|
|
|
fillcolor=TINT[farbe], color=farbe, penwidth="2.0",
|
|
|
|
|
margin="0.16,0.09")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def schreibe(punkt: graphviz.Digraph, name: str) -> None:
|
|
|
|
|
os.makedirs(BILDER, exist_ok=True)
|
Aufgeraeumt: 33 PNG-Zweitfassungen, Bau-Ueberbleibsel, Ausgabepfade
Drei Aufraeumarbeiten - und zwei Funde, die dabei auffielen.
1. DIE PNG-ZWEITFASSUNGEN SIND WEG. Jedes Diagramm lag doppelt vor, als SVG
und als PNG, und kein einziges src=/href= in der Website zeigte je auf ein
PNG. Der Build kopierte sie trotzdem mit: 3,3 MB im Repository plus 3,3 MB,
die bei jeder Veroeffentlichung auf den Webserver gingen. Die 15 Generatoren
schreiben jetzt nur noch SVG, die Docstrings sind mitgezogen. Vor dem
Loeschen geprueft: Jedes PNG hatte sein gleichnamiges SVG, alle 33 waren
versioniert.
FUND 1: erzeuge_kap06_gantt.py folgte als einziger Generator nicht der
Konvention - weder svg.hashsalt noch metadata={"Date": None}. Sein SVG trug
einen echten Zeitstempel und bei jedem Lauf andere clip-path-IDs, war also
nie byteidentisch reproduzierbar, obwohl CLAUDE.md genau das fuer alle
Generatoren festhaelt. Aufgefallen nur, weil nach der PNG-Umstellung 32 von
33 SVGs bitgleich blieben und eines nicht. Jetzt byteidentisch ueber zwei
Laeufe.
FUND 2: spiegle_bilder() legte leere Verzeichnisse auf dem Webserver an. Der
Dateifilter arbeitete korrekt, aber os.walk durchlief auch __pycache__/, und
os.makedirs() erzeugte es am Ziel. Die Verzeichnisliste wird jetzt vorher
gefiltert. Gegengetestet.
2. BAU-UEBERBLEIBSEL entfernt (alle ignoriert und neu erzeugbar):
svg-inkscape/, build_v04.log, .pytest_cache/, Programme/output/, vier
__pycache__/ und die Excel-Mappen. Arbeitsbaum 36 -> 33 MB, danach null
ignorierte Ueberbleibsel.
3. Excel_Bruecke.py SCHREIBT NEBEN DAS SKRIPT statt ins
Arbeitsverzeichnis. Es benutzte blanke relative Namen; wer es aus der
Repository-Wurzel startete, verstreute dort produktionsmix.xlsx und
produktionsmix_ergebnis.xlsx. Jetzt wie die vier anderen schreibenden
Programme ueber os.path.dirname(os.path.abspath(__file__)). Nachgemessen:
Lauf aus der Wurzel legt dort null Dateien ab.
Ausserdem git gc: 653 lose Objekte gepackt, .git von 71 MB auf 28 MB -
reines Repacken, kein Inhalt beruehrt.
Geprueft: alle 33 im Buch referenzierten SVGs vorhanden, in Quelle und
Website; 16 Generatoren fehlerfrei; keine fehlenden Bilder im LaTeX-Lauf;
33 pytest-Tests; PDF unveraendert 760 Seiten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 14:22:26 +02:00
|
|
|
for endung in ("svg",):
|
Version 04 als eigenes Repository
Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python"
(Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im
uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch
Version_03 (eingefroren) verwaltet und Version_04/ ignoriert.
Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit
der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen
Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist.
Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise,
325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren,
25 Notebooks, PDF mit 715 Seiten.
Zusaetzlich in diesem Commit:
* pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api,
figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt
unveraendert daneben bestehen. ortools steht in der Grundausstattung,
highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der
schlanken Installation gar nicht erst auftreten.
* Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py
importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo
steht in requirements.txt, wird aber von keinem Programm importiert,
sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in
[empfehlungen]).
* NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es
ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden
Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen.
* PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den
tatsaechlichen aus erzeuge_titelseite.py korrigiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
|
|
|
punkt.format = endung
|
|
|
|
|
pfad = punkt.render(os.path.join(BILDER, name), cleanup=True)
|
|
|
|
|
print(f"geschrieben: {pfad}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- Teil II: deterministische Optimierung ----------------------------------
|
|
|
|
|
|
|
|
|
|
def teil2() -> None:
|
|
|
|
|
punkt = neuer_graph("Welches Werkzeug passt zu meinem Problem? (Teil II)")
|
|
|
|
|
|
|
|
|
|
frage(punkt, "f1", "Fahrzeuge, Depots,\nZeitfenster?")
|
|
|
|
|
frage(punkt, "f2", "Ja/Nein-Entscheidungen,\nZuordnungen, Reihenfolgen?")
|
|
|
|
|
frage(punkt, "f3", "Zielfunktion und alle\nNebenbedingungen linear?")
|
|
|
|
|
frage(punkt, "f4", "Logische Regeln\n(wenn-dann, entweder-oder)\noder Zeitplanung?")
|
|
|
|
|
frage(punkt, "f5", "Nur Fluss oder\nZuordnung im Netzwerk?")
|
|
|
|
|
|
|
|
|
|
empfehlung(punkt, "routing", "OR-Tools Routing\n(Kapitel Graphen)", AMBER)
|
|
|
|
|
empfehlung(punkt, "cpsat", "CP-SAT\n(Kapitel CP-SAT)", VIOLETT)
|
|
|
|
|
empfehlung(punkt, "milp", "MILP: HiGHS / CP-SAT\n(Kapitel MILP)", CYAN)
|
|
|
|
|
empfehlung(punkt, "netz", "Spezialalgorithmus:\nMin-Cost-Flow,\nUngarische Methode", GRUEN)
|
|
|
|
|
empfehlung(punkt, "lp", "LP: scipy.linprog\noder highspy\n(Kapitel LP)", INDIGO)
|
|
|
|
|
empfehlung(punkt, "teil3", "weiter in Teil III", AMBER)
|
|
|
|
|
|
|
|
|
|
punkt.edge("f1", "routing", "ja")
|
|
|
|
|
punkt.edge("f1", "f2", "nein")
|
|
|
|
|
punkt.edge("f2", "f4", "ja")
|
|
|
|
|
punkt.edge("f2", "f3", "nein")
|
|
|
|
|
punkt.edge("f4", "cpsat", "ja")
|
|
|
|
|
punkt.edge("f4", "milp", "nein\n(Fixkosten,\nKardinalität)")
|
|
|
|
|
punkt.edge("f3", "f5", "ja")
|
|
|
|
|
punkt.edge("f3", "teil3", "nein")
|
|
|
|
|
punkt.edge("f5", "netz", "ja")
|
|
|
|
|
punkt.edge("f5", "lp", "nein")
|
|
|
|
|
|
|
|
|
|
schreibe(punkt, "teil2_solverwahl")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- Teil III: Nichtlinearitaet, Unsicherheit, Dynamik ----------------------
|
|
|
|
|
|
|
|
|
|
def teil3() -> None:
|
|
|
|
|
punkt = neuer_graph("Welches Werkzeug passt zu meinem Problem? (Teil III)")
|
|
|
|
|
|
|
|
|
|
frage(punkt, "g1", "Sind die Daten sicher\nbekannt?")
|
|
|
|
|
frage(punkt, "g2", "Entscheidung fällt in\nmehreren Stufen\nüber die Zeit?")
|
|
|
|
|
frage(punkt, "g3", "Wahrscheinlichkeiten\nbekannt?")
|
|
|
|
|
frage(punkt, "g4", "Zielfunktion konvex?\n(Quadrate, Normen,\nlog/exp konvex kombiniert)")
|
|
|
|
|
frage(punkt, "g5", "Quadratisch mit\nlinearen Nebenbedingungen?")
|
|
|
|
|
|
|
|
|
|
empfehlung(punkt, "dp", "Dynamische Programmierung\n(Bellman)", AMBER)
|
|
|
|
|
empfehlung(punkt, "stoch", "Zweistufige stochastische\nOptimierung, Szenarien", VIOLETT)
|
|
|
|
|
empfehlung(punkt, "robust", "Robuste Optimierung\n(Worst Case)", VIOLETT)
|
|
|
|
|
empfehlung(punkt, "qp", "QP über CVXPY\n(OSQP, Clarabel)", CYAN)
|
|
|
|
|
empfehlung(punkt, "konvex", "CVXPY\n— prüft Konvexität selbst", GRUEN)
|
|
|
|
|
empfehlung(punkt, "nichtkonvex", "scipy.optimize.minimize\nmit mehreren Startpunkten\n— nur lokales Optimum!", AMBER)
|
|
|
|
|
|
|
|
|
|
punkt.edge("g1", "g4", "ja")
|
|
|
|
|
punkt.edge("g1", "g2", "nein")
|
|
|
|
|
punkt.edge("g2", "dp", "ja")
|
|
|
|
|
punkt.edge("g2", "g3", "nein")
|
|
|
|
|
punkt.edge("g3", "stoch", "ja")
|
|
|
|
|
punkt.edge("g3", "robust", "nein,\nnur Bandbreiten")
|
|
|
|
|
punkt.edge("g4", "g5", "ja")
|
|
|
|
|
punkt.edge("g4", "nichtkonvex", "nein")
|
|
|
|
|
punkt.edge("g5", "qp", "ja")
|
|
|
|
|
punkt.edge("g5", "konvex", "nein")
|
|
|
|
|
|
|
|
|
|
schreibe(punkt, "teil3_solverwahl")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
teil2()
|
|
|
|
|
teil3()
|