operations_research/CLAUDE.md
dschlueter 0e2c011732 Glossar: Sprungleiste, Abkürzungen ausschreiben, CLAUDE.md-Regel
Sprungleiste (A B C ... Z) im Glossar — wie im Stichwortverzeichnis:
ein Klick je Buchstabe springt zur '## <Buchstabe>'-Überschrift.
Eingebaut in erzeuge_glossar_04.py als {=html}-Rohblock (nur HTML,
im PDF bedeutungslos).

Abkürzungen beim ersten Mal ausschreiben: 'VaR' → 'VaR (Value at Risk)',
'EVPI' → 'EVPI (Expected Value of Perfect Information)' als Link-Text
bei der ersten Nennung je Datei; danach nur die Abkürzung. 19 Abkürzungen
erfasst (VaR, CVaR, KKT, NLP, MILP, EVPI, LP, QP, ...).

CLAUDE.md: Regel 'Glossar-Links: Fachbegriffe im Fließtext automatisch
verlinkt' dokumentiert — resolve_glossar(), LINK_TEXTE,
ABBREV_ERSTE_NENNUNG, _NICHT_AUTO_LINKEN, _ZU_KURZ_ABK, .glossar-link,
{gl:}-Marke, Sprungleiste, {#gloss:<slug>}-Anchor.
2026-09-10 17:52:49 +02:00

18 KiB
Raw Blame History

CLAUDE.md

Kontext zum Repository Optimierte Entscheidungsfindung mit Python, Version 04.

Vor jeder Arbeit zuerst lesen

  • PLAN.md — das Ziel und der Weg dorthin: alle sechs Phasen, die Zielstruktur, der Kapitel-Baukasten, die dreizehn verbindlichen Regeln, die Verifikationsschritte. Ändert sich nur bei einem Kurswechsel.
  • PROGRESS.md — der erreichte Stand: eine Checkliste zur Wiederaufnahme, die Referenzwerte zum Gegenprüfen, was erledigt ist, welche Funde es unterwegs gab und was der konkret nächste Schritt ist. Wird nach jedem Arbeitsschritt fortgeschrieben.

Ohne diese beiden Dateien fehlt der Kontext, um sinnvoll weiterzuarbeiten — die Kapiteltexte allein verraten weder den Stand noch die Konventionen. Alle sechs Phasen sind abgeschlossen; PROGRESS.md Abschnitt 7 nennt, was bewusst offen geblieben ist und warum.


Struktur

Dieses Verzeichnis ist die Wurzel; alle Skripte leiten ihre Pfade daraus ab (BASIS = dirname(dirname(__file__)) bzw. dirname(HIER)).

Operations_Research_mit_Python_Version_04/     Quelle: 37 Kapiteldateien + Build-Skripte
                                               + NEUER_TITEL.md (Titeltext)
bilder_04/                                     Quelle: Diagramme + erzeuge_*.py-Generatoren
web_04/                                        Quelle: site.css, site.js, icons.svg,
                                               plotly.min.js, katex/ - die statischen
                                               Bestandteile der Website
pandoc/, pandoc-defaults-basis.yaml, pandoc-defaults-buch.yaml   PDF-Konfiguration
                                               (pandoc/emoji_fallback.lua schaltet Zeichen,
                                               die Times New Roman fehlt, auf Symbola um -
                                               siehe PROGRESS.md, "Sonderzeichen in der
                                               PDF-Fassung")
Operations_Research_mit_Python_Version_04.md   generiert: Gesamtdokument
Operations_Research_mit_Python_Version_04.pdf  generiert: PDF (xelatex)
OR_HTML_04/                                    generiert: Mehrseiten-Website
Operations_Research_mit_Python_Version_04_Programme/  generiert: Beispielprogramme
Notebooks_04/                                  generiert: ein .ipynb je Kapitel
pyproject.toml                                 Quelle: Abhängigkeiten in Gruppen
Dockerfile, .dockerignore                      Quelle: Kurs-Image (Programme + JupyterLab)
LICENSE, LICENSE-TEXT.md                       MIT fuer Code, CC BY-SA 4.0 fuer den Text
.gitattributes                                 LF im Repository, egal auf welchem System
.env.beispiel                                  Vorlage; die echte .env (Ziel des Uploads)
                                               ist bewusst NICHT im Repository
pandoc-defaults-basis.yaml, pandoc/, pandoc-defaults-buch.yaml   PDF-Konfiguration

Nur die als Quelle markierten Verzeichnisse werden von Hand bearbeitet. Seit dem Umzug nach web_04/ stimmt dieser Satz auch: Vorher lagen site.css, site.js, icons.svg und katex/ mitten im erzeugten OR_HTML_04/. --check bewacht das jetzt (pruefe_assets()), und der Bau bricht ab, wenn jemand die Kopie statt der Quelle bearbeitet hat.

Dieses Verzeichnis ist seit dem 08.09.2026 ein eigenes Git-Repository (main). Das übergeordnete OR_mit_Python/ ist nur noch das Archiv der Historie bis zur Trennung und verwaltet aktiv allein Version_03; es trägt Version_04/ in seiner .gitignore. Commits gehören ab jetzt hierher.

Build

cd Version_04
python3 Operations_Research_mit_Python_Version_04/build_version_04.py --check    # nur prüfen
python3 Operations_Research_mit_Python_Version_04/build_version_04.py --pdf --html
python3 Operations_Research_mit_Python_Version_04/extract_programme_04.py
python3 Operations_Research_mit_Python_Version_04/erzeuge_glossar_04.py     # Anhang E
python3 Operations_Research_mit_Python_Version_04/veroeffentliche_04.py           # hochladen

veroeffentliche_04.py lädt OR_HTML_04/ per rsync --delete nach https://jamulix.de/OR/nicht per scp, denn das überschreibt nur und löscht nie; eine aus dem Buch entfernte Seite bliebe sonst dauerhaft online. Vorher prüft es, ob eine Quelldatei neuer ist als die gebaute Website (die Website hing einmal wochenlang einen Bau zurück, ohne dass es auffiel), hinterher ruft es dreizehn Adressen per HTTP ab. Bei mehr als 30 Löschungen bricht es ab — die Liste ist dann anzusehen, bevor --loeschgrenze N sie freigibt. Ziel in .env (nicht im Repository, Vorlage .env.beispiel); Anmeldung per SSH-Schlüssel, kein Passwort.

--check prüft mehr als die Struktur: fehlende Codezäune, Links auf nicht existierende Dateien und harte Kapitel-/Abschnitts-/Aufgabennummern im Quelltext. Es meldet Datei und Zeile. Der Suchausdruck erlaubt beliebigen Zwischenraum — der letzte gefundene Fall war ein Handrechnung\n12.1 über zwei Zeilen, an dem jede zeilenweise Suche vorbeiläuft.

build_version_04.py fasst nicht notwendige harte Zeilenumbrüche innerhalb von Absätzen zusammen (reflow_markdown()) — viele Markdown-Renderer stellen einen einzelnen Umbruch sonst fälschlich als sichtbaren Umbruch dar.


Die wichtigsten Konventionen

Keine abgeleiteten Zahlen im Quelltext. Das ist die Regel, an der dieses Projekt am häufigsten gescheitert ist — zehn Mal an verschiedenen Stellen verletzt gefunden (Anhang A, 66 Programm-Docstrings, die Übersichtstabellen der Anhänge, CLAUDE.md selbst, eine Tabellenzelle, die 98 Lösungsmarken des Anhangs, neun Denkfehler-Verweise, sieben „Übung N.N"-Verweise in alter Zählung, die Dateiübersicht des Leser-Wegweisers, zuletzt der Titeltext der Startseite). Seit dem letzten Fund trägt keine Marke mehr eine handgeschriebene Nummer, und --check bewacht jede Familie. Konkret:

  • Überschriften: # Kapitel: <Titel> {#kap:<label>}, ## Titel {#sec:<label>} — die Nummer vergibt der Build (resolve_numbering(), nummeriere_abschnitte()).
  • Aufgaben, Handrechnungen, Abbildungen, Micro-Quiz: **Aufgabe ⭐ — Titel.**, > **✏️ Handrechnung: Titel**, ![Bildunterschrift](bilder_04/…), > **❓ Micro-Quiz: Titel** — Nummern von nummeriere_marken().
  • Lösungen in Anhang A: **{loesung} — Titel.** Der Präfix kommt nicht aus # Anhang A:, sondern aus dem Kapitel des umgebenden {#sec:loesungen-<X>}-Abschnitts — die Zuordnung sec:loesungen-<X>kap:<X> gilt für alle 23. --check zählt zusätzlich ab, dass es je Kapitel so viele Lösungen wie Aufgaben gibt.
  • Der Denkfehler bekommt bewusst gar keine Nummer. Es gibt je Kapitel genau einen, unter einer bereits nummerierten Überschrift. Verwiesen wird auf {ref:sec:<kapitel>-denkfehler}.
  • Im Fließtext {ref:<label>}, nie eine Literalzahl.
  • Das Glossar (Anhang E) wird erzeugt. Die 239 Einträge stehen in glossar_eintraege_04.py als Viertupel (Anzeigename, Indexmarke, Definition, Verweisziel); erzeuge_glossar_04.py schreibt daraus 94_Anhang_Glossar.md. Die .md nicht von Hand bearbeiten — --check bemerkt es und nennt die Quelle. Der Erzeuger prüft außerdem gegen das Buch: Jede Indexmarke muss dort buchstabengetreu vorkommen (sonst entstünde ein zweiter Registereintrag für dasselbe Konzept), jedes {ref:}-Ziel muss existieren, und kein Verweis darf auf das Glossar selbst zeigen. Begriffe, die es nur im Glossar gibt, stehen ausdrücklich in NUR_IM_GLOSSAR — sonst ließe sich ein solcher Fall nicht von einem Tippfehler unterscheiden.
  • Glossar-Links: Fachbegriffe im Fließtext werden automatisch verlinkt. resolve_glossar() in build_version_04.py sucht beim Bau jeden Fließtext nach Glossar-Begriffen ab und ersetzt die erste Nennung je Begriff je Datei durch einen Link auf den Glossar-Eintrag (#gloss:<slug> im Gesamtdokument, anhang-glossar.md#gloss:<slug> in den Einzelseiten). Die .md-Quelldateien enthalten keine manuellen Link-Marken — der Build macht es automatisch, sodass Textänderungen nichts nachziehen. Ausgenommen vom Auto-Linking: Code-Blöcke, Inline-Code, Marken ({idx:}, {ref:}), Markdown-Links und Überschriften. Die Slug-Map LINK_TEXTE und die Erste-Nennung-Map ABBREV_ERSTE_NENNUNG werden in glossar_eintraege_04.py automatisch aus EINTRAEGE generiert. Abkürzungen (VaR, CVaR, KKT …) werden beim ersten Vorkommen ausgeschrieben: „VaR (Value at Risk)“ als Link-Text, danach nur „VaR“. Mehrdeutige Begriffe („Lösung“, „Matrix“, „Variable“ …), die im Glossar stehen aber nicht jedes Vorkommen verlinkt werden soll, stehen in _NICHT_AUTO_LINKEN; wer sie an einer Stelle trotzdem verlinken will, setzt {gl:Begriff}. Zu kurze Abkürzungen (OR — taucht als Teilstring in OR-Tools auf) stehen in _ZU_KURZ_ABK und werden nur mit {gl:OR} verlinkt. Die CSS-Klasse .glossar-link (dezente gestrichelte Unterstreichung, nicht blau) steht in web_04/assets/site.css. erzeuge_glossar_04.py gibt jedem Eintrag einen {#gloss:<slug>}-Anchor und eine alphabetische Sprungleiste (A B C …) wie das Stichwortverzeichnis.
  • Der Titeltext steht in Operations_Research_mit_Python_Version_04/NEUER_TITEL.md und nirgends sonst. titeltexte_04.py liest ihn; die PDF-Titelseite (bilder_04/erzeuge_titelseite.py) und der Hero-Block der Startseite (baue_landingpage()) speisen sich daraus. Eine Titeländerung wird einmal dort gemacht. Das Vorwort trägt denselben Text als Buchprosa — pruefe_titeltexte() vergleicht ihn bei jedem --check mit der Quelle.
  • Die Dateiübersicht in Operations_Research_mit_Python_Version_04/README.md steht zwischen <!-- KAPITELTABELLE:ANFANG --> und <!-- KAPITELTABELLE:ENDE --> und wird von schreibe_kapiteltabelle() aus derselben Struktur erzeugt wie die Nummern im Buch. Nicht von Hand bearbeiten — --check meldet einen veralteten Block, ein Lauf ohne --check zieht ihn nach.
  • In Code-Kommentaren und Docstrings der Kapitelname („Kapitel Metaheuristiken:"), nie die Nummer. Auch nicht im Dateinamen.

Querverweise für die Website laufen über baue_seiten_registry() / resolve_numbering_seite(), nicht über resolve_numbering() — nur dort entsteht aus einem seitenübergreifenden Verweis andere-seite.html#anker.

Stichwortregister: {idx:Begriff} bzw. {idx:Oberbegriff!Unterbegriff}. Vor einem neuen Begriff prüfen, ob er schon existiert: grep -ohE '\{idx:[^}]+\}' Operations_Research_mit_Python_Version_04/*.md | sort -u — sonst entstehen zwei Registereinträge für dasselbe Konzept. texindy sortiert mit dem deutschen din5007-Modul (Umlaute wie im Telefonbuch); das generische -L german bricht ab.

Programme sind ein Artefakt, keine zweite Quelle. Änderungen gehören in den Kapitel-Codeblock, danach extract_programme_04.py. Ein Codeblock gilt als vollständiges Programm, wenn er mit #!/usr/bin/env python3, einer Leerzeile und # Name.py beginnt. Auch das README.md im Programme-Verzeichnis wird erzeugt — aus der Vorlage README_Programme.md; requirements.txt ist die einzige dort von Hand gepflegte Datei.

or_kern.py ist der gemeinsame Unterbau aller Programme (Domänenmodell, SolverStatus, Loesung-DTO, Abnahmeprüfung). Abgedruckt im Kapitel Praxisfallen.

ortools und highspy lassen sich nicht im selben Prozess importieren (beide bringen eine eigene HiGHS-Kopie mit). Deshalb lädt or_kern.py Solverbibliotheken erst in der aufrufenden Funktion, und Programme, die beide brauchen, starten getrennte Prozesse (Muster: Ein_System_Vier_Ansaetze.py, Solverwechsel_CPSAT_HiGHS.py). cvxpy zieht ein installiertes highspy bei der Solver-Erkennung selbst mit hinein — der Konflikt entsteht also auch indirekt.

Neue Kapiteldatei ⇒ in die DATEIEN-Liste in build_version_04.py. Neues Programm ⇒ drei Stellen: Kapitelkopf („Programme:"), Vorwort („Verzeichnis der Beispielprogramme"), bei neuer Abhängigkeit requirements.txt und pyproject.toml (dort in die passende Gruppe, nicht pauschal in die Grundausstattung).

Die vollständigen dreizehn Regeln stehen in PLAN.md Abschnitt 9 — darunter, dass jede abgedruckte Ausgabe aus einem echten Lauf stammt und dass PROGRESS.md in denselben Commit gehört wie die Arbeit, die sie beschreibt.


Diagramme

33 Generatoren bilder_04/erzeuge_*.py erzeugen 38 der 48 Kapiteldiagramme, jeweils aus derselben Instanz wie das zugehörige Buchprogramm. 17 davon benutzen bilder_04/stil_04.py — Palette, rcParams und speichere() an einer Stelle statt siebzehnmal; die älteren setzen svg.hashsalt und metadata={"Date": None} weiterhin selbst. (Das 49. SVG im Verzeichnis ist titelseite.svg, die der Build bei jedem Lauf selbst schreibt und die im Buch nicht als Abbildung erscheint.) Das ist kein Selbstzweck: Von zehn nachgebauten Bildern förderten sieben einen Fehler zutage — dreimal ein Modell, das im Buch gar nicht vorkommt, einmal widersprüchliche Zahlen zwischen Bild und Text, einmal ein gekipptes Vorzeichen. Wer ein Diagramm anfasst, vergleicht es zuerst mit dem Modell des Kapitels.

Die übrigen 10 sind schematisch (Kästen, Pfeile, beschriftete Formeln) und bekommen bewusst keinen Generator — dort kann nichts driften. Das Kriterium steht in PROGRESS.md Abschnitt 6c. Es gilt aber nur, solange ein Bild keine Zahl und keine Aufzählung aus dem Text wiederholt: kap15_or_plattform.svg war handgezeichnet und zeigte fünf Schichten neben einer Tabelle mit sechs — seither wird es aus eben dieser Tabelle erzeugt.

Ein neues Bild ergänzt in der Regel, es ersetzt nicht. Bei den neun Überarbeitungen der Stufe 2 war nur eines wirklich ein Ersatz (die OR-Architektur); in den übrigen Fällen zeigte das vorhandene Diagramm etwas anderes — die Sehnenbedingung neben der Talkarte, den Suchbaum neben der Polyedergeometrie, die Verteilung neben der Subadditivität. Vor dem Überschreiben eines SVG also prüfen, was es zeigt, statt nur, wo es steht.

Konventionen der Generatoren: plt.rcParams["svg.hashsalt"] = "or-mit-python-v04" und metadata={"Date": None} für byteidentische Läufe — beides, sonst trägt das SVG einen Zeitstempel und wechselnde clip-path-IDs. Geschrieben wird nur SVG; PNG-Zweitfassungen gab es einmal, sie wurden nirgends referenziert; stammt die Instanz aus einem Zufallsstrom, wird die Ziehungsreihenfolge des Buchprogramms nachgespielt.

Plotly-Figuren

{plotly:name} bindet bilder_04/plotly/<name>.html in die Kapitelseite ein. Das Fragment wird als Rohblock ```{=html} ausgegeben — nicht als blankes HTML: Plotlys Fragment ist eine einzige lange Zeile mit eingebetteten Leerzeichenketten, aus der Pandoc sonst einen Codeblock macht. Genau daran waren alle vier Figuren kaputt, bis es die Schlussabnahme fand. Im PDF steht stattdessen ein Hinweis auf die Website.

Karten

{karte:name} bindet bilder_04/karten/<name>.html als <iframe> ein — anders als bei Plotly nicht inline, weil Leaflets Stylesheet sonst in die Buchseite hineingriffe. Im PDF steht wieder ein Hinweis auf die Website. --check prüft für beide Markenarten, dass die Datei existiert.

Erzeugt werden die Karten von demselben bilder_04/erzeuge_*.py, das auch das zugehörige SVG schreibt — aus einem Lauf, sonst zeigten die beiden Darstellungen bei einem heuristischen Solver womöglich verschiedene Lösungen. Zwei Vorkehrungen in erzeuge_vrp_touren.py sind übertragbar:

  • Byte-Reproduzierbarkeit: Folium vergibt jedem Element eine uuid4. normalisiere() ersetzt sie durch fortlaufende Nummern — dieselbe Sorgfalt wie svg.hashsalt bei den matplotlib-Bildern.
  • Netzunabhängigkeit: Von Foliums zehn CDN-Einbindungen bleiben die zwei gebrauchten, umgeschrieben auf ../../assets/leaflet.* (mitgeliefert in web_04/assets/); die übrigen acht entfallen, weil die Karte nur CircleMarker, PolyLine und Tooltip benutzt. Ohne Netz fehlen nur die Kartenkacheln.

Die Instanz ist synthetisch und bleibt es. projiziere() legt das 100 × 100-km-Raster azimutal-äquidistant auf eine reale Region; pruefe_projektion() bricht ab, wenn die Abstände dabei um mehr als 0,01 % verzerrt würden (gemessen: 0,001 %). Kein abgedruckter Wert ändert sich dadurch — das war die Bedingung, unter der die Karte überhaupt möglich wurde.


Veroeffentlichung

Das Buch steht online auf https://jamulix.de/OR/, das Repository oeffentlich auf https://kitux.de/forgejo/dschlueter/operations_research.

Die Website hat vier Übersichtsseiten: index.html (Landing), programme.html, notebooks.html und stichwortverzeichnis.html. Die beiden Downloadverzeichnisse programme/ und Notebooks_04/ bekommen je eine index.html, die auf ihre Übersicht weiterleitet — sonst antwortet der Webserver mit 403, weil Directory-Listing abgeschaltet ist.

Nach jedem --pdf --html gehoert veroeffentliche_04.py hinterher — sonst laeuft die veroeffentlichte Fassung dem Repository hinterher, und genau das ist einmal wochenlang unbemerkt geblieben. Von Hand muss dabei nichts mehr geprueft werden: Das Skript weigert sich, einen veralteten Bau hochzuladen, und ruft anschliessend vierzehn Adressen ab.

Der erste Lauf am 08.09.2026 uebertrug 213 Dateien und loeschte 35 — 33 verwaiste PNGs, ein leeres __pycache__/ und die geteilte Anhangseite. Alle 35 haette ein scp-Upload stehen gelassen.

COLAB_BASIS_URL ist bewusst leer. Colab oeffnet Notebooks nur aus GitHub, Google Drive oder einem Upload; die URL-Form colab.research.google.com/github/... ist fest auf GitHub verdrahtet und kann eine selbstgehostete Git-Instanz nicht lesen. Statt eines toten Knopfes bekommt jedes Kapitel einen Download-Link auf sein Notebook. Wer spaeter nach GitHub spiegelt, traegt dort einen Wert ein.

Wer ohne eigene Installation rechnen will, nimmt das Kurs-Image: docker run --rm -p 127.0.0.1:8888:8888 or-mit-python startet JupyterLab mit allen 25 Notebooks. Ohne Portfreigabe und mit einem Programmnamen als Argument fuehrt dasselbe Image ein einzelnes Programm aus.


Sprache

Kommentare, Ausgaben und Fließtext sind durchgängig Deutsch — diesen Stil beibehalten.