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>
This commit is contained in:
parent
3e5c6fc627
commit
360912dbfa
49 changed files with 322 additions and 248 deletions
|
|
@ -4,8 +4,8 @@
|
|||
"""
|
||||
Erzeugt die beiden Architekturdiagramme fuer den Weg vom Skript zum Betrieb:
|
||||
|
||||
bilder_04/kap_praxisfallen_schichten.svg / .png Schichten von or_kern.py
|
||||
bilder_04/kap_testing_dienst.svg / .png der Dienst und sein Ablauf
|
||||
bilder_04/kap_praxisfallen_schichten.svg Schichten von or_kern.py
|
||||
bilder_04/kap_testing_dienst.svg der Dienst und sein Ablauf
|
||||
|
||||
Beide bilden ab, was in den Programmen tatsaechlich steht - nicht ein
|
||||
allgemeines Architekturschema:
|
||||
|
|
@ -62,7 +62,7 @@ def kasten(punkt: graphviz.Digraph, name: str, text: str, farbe: str,
|
|||
|
||||
def schreibe(punkt: graphviz.Digraph, name: str) -> None:
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
punkt.format = endung
|
||||
pfad = punkt.render(os.path.join(BILDER, name), cleanup=True)
|
||||
print(f"geschrieben: {pfad}")
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"""
|
||||
Erzeugt den Branch-and-Bound-Suchbaum zum MILP-Kapitel:
|
||||
|
||||
bilder_04/kap_milp_suchbaum.svg / .png
|
||||
bilder_04/kap_milp_suchbaum.svg
|
||||
|
||||
Gezeichnet wird der Baum der Handrechnung des Kapitels:
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ def zeichne(knoten, kanten, bestes, zielwert, geloest) -> None:
|
|||
color="#94a3b8", fontsize="10", margin="0.18,0.12")
|
||||
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
punkt.format = endung
|
||||
pfad = punkt.render(os.path.join(BILDER, "kap_milp_suchbaum"), cleanup=True)
|
||||
print(f"geschrieben: {pfad}")
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
Erzeugt die Figur "Was die Hausregeln kosten" zum Kapitel Markowitz:
|
||||
|
||||
bilder_04/kap_markowitz_restriktionen.svg statisch, fuer das PDF
|
||||
bilder_04/kap_markowitz_restriktionen.png Rasterfassung
|
||||
bilder_04/plotly/kap_markowitz_restriktionen.html interaktiv, fuer die Website
|
||||
|
||||
Gezeigt werden zwei Effizienzlinien desselben Universums: einmal ohne
|
||||
|
|
@ -210,7 +209,7 @@ def zeichne_statisch(frei, gebunden, abstand, risiko_stelle) -> None:
|
|||
figur.tight_layout()
|
||||
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
pfad = os.path.join(BILDER, f"kap_markowitz_restriktionen.{endung}")
|
||||
figur.savefig(pfad, format=endung, dpi=160,
|
||||
metadata={"Date": None} if endung == "svg" else None)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
Erzeugt das Gantt-Diagramm zum Job-Shop-Beispiel aus Kapitel 6 in zwei Fassungen:
|
||||
|
||||
bilder_04/kap06_jobshop_gantt.svg statisch, fuer das PDF
|
||||
bilder_04/kap06_jobshop_gantt.png statisch, Rasterfassung
|
||||
bilder_04/plotly/kap06_jobshop_gantt.html interaktiv, fuer die Website
|
||||
|
||||
Die Daten sind identisch mit JobShop_Intervalle.py; geloest wird hier erneut,
|
||||
|
|
@ -27,6 +26,11 @@ import matplotlib.pyplot as plt
|
|||
import plotly.graph_objects as go
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# Reproduzierbare SVG-Ausgabe (siehe erzeuge_titelseite.py). Dieser Generator
|
||||
# war als einziger ohne beides - sein SVG trug deshalb einen echten
|
||||
# Zeitstempel und bei jedem Lauf andere clip-path-IDs.
|
||||
plt.rcParams["svg.hashsalt"] = "or-mit-python-v04"
|
||||
|
||||
# Identisch zu JobShop_Intervalle.py
|
||||
AUFTRAEGE = [
|
||||
[(0, 3), (1, 2), (2, 2)], # Auftrag 0
|
||||
|
|
@ -111,9 +115,10 @@ def zeichne_statisch(plan, makespan: int) -> None:
|
|||
figur.tight_layout()
|
||||
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
pfad = os.path.join(BILDER, f"kap06_jobshop_gantt.{endung}")
|
||||
figur.savefig(pfad, format=endung, dpi=160)
|
||||
figur.savefig(pfad, format=endung, dpi=160,
|
||||
metadata={"Date": None} if endung == "svg" else None)
|
||||
print(f"geschrieben: {pfad}")
|
||||
plt.close(figur)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"""
|
||||
Erzeugt das KKT-Diagramm zum QP/NLP-Kapitel:
|
||||
|
||||
bilder_04/kap_qp_kkt.svg / .png
|
||||
bilder_04/kap_qp_kkt.svg
|
||||
|
||||
Zwei Bilder nebeneinander:
|
||||
|
||||
|
|
@ -216,7 +216,7 @@ if __name__ == "__main__":
|
|||
zeichne_2d(rechts, x_stern, lam)
|
||||
figur.tight_layout()
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
pfad = os.path.join(BILDER, f"kap_qp_kkt.{endung}")
|
||||
figur.savefig(pfad, format=endung, dpi=160,
|
||||
metadata={"Date": None} if endung == "svg" else None)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
Erzeugt das Konditionszahl-Diagramm zum Kapitel Fundament:
|
||||
|
||||
bilder_04/kap_fundament_kondition.svg statisch, fuer das PDF
|
||||
bilder_04/kap_fundament_kondition.png Rasterfassung
|
||||
|
||||
Zwei Aussagen, beide gerechnet und nicht behauptet:
|
||||
|
||||
|
|
@ -185,7 +184,7 @@ def zeichne(kappas, mediane, maxima, kappa_vorher, kappa_nachher) -> None:
|
|||
|
||||
figur.tight_layout()
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
pfad = os.path.join(BILDER, f"kap_fundament_kondition.{endung}")
|
||||
figur.savefig(pfad, format=endung, dpi=160,
|
||||
metadata={"Date": None} if endung == "svg" else None)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
Erzeugt das Suchverlauf-Diagramm zum Kapitel Metaheuristiken:
|
||||
|
||||
bilder_04/kap_metaheuristiken_landschaft.svg statisch, fuer das PDF
|
||||
bilder_04/kap_metaheuristiken_landschaft.png Rasterfassung
|
||||
bilder_04/plotly/kap_metaheuristiken_verlauf.html interaktiv, fuer die Website
|
||||
|
||||
Eine Suchlandschaft laesst sich bei 200 Auftraegen nicht zeichnen - der Raum
|
||||
|
|
@ -223,7 +222,7 @@ def zeichne(verlaeufe, start, temperaturreihe) -> None:
|
|||
|
||||
figur.tight_layout()
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
pfad = os.path.join(BILDER, f"kap_metaheuristiken_landschaft.{endung}")
|
||||
figur.savefig(pfad, format=endung, dpi=160,
|
||||
metadata={"Date": None} if endung == "svg" else None)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"""
|
||||
Erzeugt das Flussdiagramm zum Graphen-Kapitel:
|
||||
|
||||
bilder_04/kap_graphen_min_cost_flow.svg / .png
|
||||
bilder_04/kap_graphen_min_cost_flow.svg
|
||||
|
||||
Das Vorgaengerbild zeigte die Instanz von Min_Cost_Flow.py korrekt - aber nur
|
||||
die Instanz. Kosten und Kapazitaeten standen an den Kanten, der FLUSS nicht.
|
||||
|
|
@ -128,7 +128,7 @@ def zeichne(gesamtkosten: float, fluss: np.ndarray, preise: np.ndarray) -> None:
|
|||
penwidth=f"{1.0 + 3.0 * menge / kapazitaet:.1f}")
|
||||
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
punkt.format = endung
|
||||
pfad = punkt.render(os.path.join(BILDER, "kap_graphen_min_cost_flow"),
|
||||
cleanup=True)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
Erzeugt das Pareto-Front-Diagramm zum Mehrziel-Kapitel in zwei Fassungen:
|
||||
|
||||
bilder_04/kap_mehrziel_pareto.svg statisch, fuer das PDF
|
||||
bilder_04/kap_mehrziel_pareto.png statisch, Rasterfassung
|
||||
bilder_04/plotly/kap_mehrziel_pareto.html interaktiv, fuer die Website
|
||||
|
||||
Die Instanz ist identisch mit Mehrziel_Pareto.py, und gerechnet wird hier
|
||||
|
|
@ -140,7 +139,7 @@ def zeichne_statisch(front, huelle) -> None:
|
|||
figur.tight_layout()
|
||||
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
pfad = os.path.join(BILDER, f"kap_mehrziel_pareto.{endung}")
|
||||
figur.savefig(pfad, format=endung, dpi=160,
|
||||
metadata={"Date": None} if endung == "svg" else None)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
"""
|
||||
Erzeugt die beiden Polyeder-Diagramme zum Fundament- und zum LP-Kapitel:
|
||||
|
||||
bilder_04/kap_fundament_polyeder.svg / .png der zulaessige Bereich
|
||||
bilder_04/kap_lp_simplex_ecken.svg / .png der Eckenlauf des Simplex
|
||||
bilder_04/kap_fundament_polyeder.svg der zulaessige Bereich
|
||||
bilder_04/kap_lp_simplex_ecken.svg der Eckenlauf des Simplex
|
||||
|
||||
Beide zeigen DASSELBE Modell - das Bot-Allokationsproblem, das sich durch die
|
||||
ersten Kapitel zieht:
|
||||
|
|
@ -165,7 +165,7 @@ def zeichne_rahmen(achse, ecken: np.ndarray) -> None:
|
|||
|
||||
def schreibe(figur, name: str) -> None:
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
pfad = os.path.join(BILDER, f"{name}.{endung}")
|
||||
figur.savefig(pfad, format=endung, dpi=160,
|
||||
metadata={"Date": None} if endung == "svg" else None)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
Erzeugt das Diagramm "Warum Runden scheitert" zum MILP-Kapitel:
|
||||
|
||||
bilder_04/kap_milp_runden.svg statisch, fuer das PDF
|
||||
bilder_04/kap_milp_runden.png Rasterfassung
|
||||
|
||||
Zwei Bilder nebeneinander, beide aus dem Kapitel selbst:
|
||||
|
||||
|
|
@ -213,7 +212,7 @@ def zeichne(zeilen) -> None:
|
|||
figur.tight_layout()
|
||||
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
pfad = os.path.join(BILDER, f"kap_milp_runden.{endung}")
|
||||
figur.savefig(pfad, format=endung, dpi=160,
|
||||
metadata={"Date": None} if endung == "svg" else None)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
Erzeugt die beiden Entscheidungs-Flussdiagramme "Welches Werkzeug passt zu
|
||||
meinem Problem?" fuer die Auftakte von Teil II und Teil III:
|
||||
|
||||
bilder_04/teil2_solverwahl.svg / .png deterministische Verfahren
|
||||
bilder_04/teil3_solverwahl.svg / .png Nichtlinearitaet und Unsicherheit
|
||||
bilder_04/teil2_solverwahl.svg deterministische Verfahren
|
||||
bilder_04/teil3_solverwahl.svg Nichtlinearitaet und Unsicherheit
|
||||
|
||||
Beide entstehen mit Graphviz. Die Farben folgen der Buchpalette
|
||||
(Indigo, Cyan, Violett, Gruen, Amber), die Kastenform codiert die Rolle:
|
||||
|
|
@ -57,7 +57,7 @@ def empfehlung(punkt: graphviz.Digraph, name: str, text: str, farbe: str) -> Non
|
|||
|
||||
def schreibe(punkt: graphviz.Digraph, name: str) -> None:
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
punkt.format = endung
|
||||
pfad = punkt.render(os.path.join(BILDER, name), cleanup=True)
|
||||
print(f"geschrieben: {pfad}")
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
Erzeugt das VaR/CVaR-Diagramm zum Kapitel CVaR:
|
||||
|
||||
bilder_04/kap_cvar_var_vergleich.svg statisch, fuer das PDF
|
||||
bilder_04/kap_cvar_var_vergleich.png Rasterfassung
|
||||
|
||||
Gezeigt wird die Verteilung simulierter Tagesrenditen mit beiden Kennzahlen:
|
||||
Der VaR ist die SCHWELLE, unter die nur 5 % der Tage fallen; der CVaR ist der
|
||||
|
|
@ -142,7 +141,7 @@ def zeichne(werte: np.ndarray, var: float, cvar: float) -> None:
|
|||
figur.tight_layout()
|
||||
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
pfad = os.path.join(BILDER, f"kap_cvar_var_vergleich.{endung}")
|
||||
figur.savefig(pfad, format=endung, dpi=160,
|
||||
metadata={"Date": None} if endung == "svg" else None)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"""
|
||||
Erzeugt die Tourenkarte zum Graphen-Kapitel:
|
||||
|
||||
bilder_04/kap_graphen_vrp_touren.svg / .png
|
||||
bilder_04/kap_graphen_vrp_touren.svg
|
||||
|
||||
Das Vorgaengerbild war ein Schema: zwei Touren, vier Kunden, Zeitfenster in
|
||||
Uhrzeiten. Die Instanz des Kapitels hat 16 Kunden, vier Fahrzeuge und
|
||||
|
|
@ -170,7 +170,7 @@ def zeichne(koordinaten, touren) -> None:
|
|||
figur.tight_layout()
|
||||
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
pfad = os.path.join(BILDER, f"kap_graphen_vrp_touren.{endung}")
|
||||
figur.savefig(pfad, format=endung, dpi=160, bbox_inches="tight",
|
||||
metadata={"Date": None} if endung == "svg" else None)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
Erzeugt das Wasserfalldiagramm zum Kapitel Supply-Chain und Energieeinsatz:
|
||||
|
||||
bilder_04/kap_supplychain_wirkung.svg statisch, fuer das PDF
|
||||
bilder_04/kap_supplychain_wirkung.png Rasterfassung
|
||||
|
||||
Die Frage, die es beantwortet, ist die des Managements: WOFUER genau kostet
|
||||
der Erwartungswert-Plan 149 % mehr? Die Tabelle im Kapitel nennt nur die
|
||||
|
|
@ -228,7 +227,7 @@ def zeichne(zwei: dict, erw: dict) -> None:
|
|||
|
||||
figur.tight_layout()
|
||||
os.makedirs(BILDER, exist_ok=True)
|
||||
for endung in ("svg", "png"):
|
||||
for endung in ("svg",):
|
||||
pfad = os.path.join(BILDER, f"kap_supplychain_wirkung.{endung}")
|
||||
figur.savefig(pfad, format=endung, dpi=160,
|
||||
metadata={"Date": None} if endung == "svg" else None)
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 63 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 94 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 88 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 80 KiB |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue