operations_research/README.md
dschlueter 0f9c815b86 Grafik-Upgrade Stufe 1: elf neue Diagramme
Grafik_Upgrade.md verlangt "37 didaktisch wertvolle Visualisierungen". Es sind
19: Die Abschnitte "Modul 1-4" wiederholen die Grafiken 1-18 unter den Nummern
19-36, nur Grafik 37 ist zusaetzlich. Belegt an den Skriptnamen - 38
Nennungen, 19 verschiedene Dateien, erzeuge_kombinatorik_wand.py steht dreimal
darin. Von den 19 betreffen neun bereits vorhandene Diagramme.

Umgesetzt sind die zehn wirklich neuen plus eine elfte, die aus einem
Sachfehler des Auftrags entstand: Grafik 14 sollte in Abschnitt 12.3 stehen,
nannte aber Zahlen aus Kapitel 17. Beide Stellen haben jetzt ihre eigene
Grafik mit ihren eigenen Zahlen.

Vier Vorgaben des Auftrags wurden bewusst nicht befolgt: PNG bei 300 dpi (das
Buch kennt nur SVG - die 33 verwaisten PNGs waren gerade erst geloescht
worden), Matplotlib-Standardfarben (die Buchpalette steht in 33 Diagrammen),
harte Kapitelnummern im Fliesstext ({ref:sec:...} stattdessen) und die
fehlende Byte-Reproduzierbarkeit.

Der eigentliche Ertrag waren die Selbsttests. Jeder Generator prueft seine
Zahlen gegen das, was im Kapitel steht, und bricht bei Abweichung ab. Vier
haben angeschlagen:

* Kombinatorik-Wand: 25! ergibt 491,5 Mio. Jahre, die Tabelle nennt 490 Mio. -
  Rundung auf zwei signifikante Stellen, nicht Fehler des Buchs.
* Laufzeit-Diagramm: Die abgedruckte Prozentspalte laesst sich aus den
  abgedruckten Zeiten nicht exakt nachrechnen (0,001/0,008 ergibt 12 %,
  gedruckt sind 14 %) - die Zeiten sind gerundet, die Prozente nicht.
* Predict-then-Optimize: geratene Datenaufteilung ergab 197,35 statt 200,95
  MSE. Mit TRAINING aus dem Buchprogramm stimmen alle vier Kostenwerte.
* Almgren-Chriss: ein echter Fehler im Buchprogramm.

DER FUND: analytische_loesung() minimiert die eigene Kostenfunktion nicht. Ihr
Pfad kostet 10.860,40 EUR, der DP-Pfad 10.266,24 EUR - ein Gitterverfahren
kann das kontinuierliche Optimum aber nicht unterbieten. periodenkosten()
rechnet das Risiko mit lambda/2, die geschlossene Formel setzt
kappa~^2 = lambda*sigma^2*P0^2/eta ohne dieses Halbe. Mit lambda/2 liefert die
Formel 10.264,85 EUR und denselben Pfad wie die DP, knapp UNTER der
Gitterloesung. Die Grafik zeigt bis zur Entscheidung darueber den DP-Pfad; er
ist der, den das Kapitel abdruckt.

Nachweise: Alle elf Diagramme sind ueber zwei Laeufe byte-identisch. Kein
vorhandenes Diagramm wurde veraendert. Die Lastabwurf-Grafik reproduziert die
Befundtabelle exakt (874.870/2.504.154/28 von 40 gegen 351.356/415.924/0).

Zwei Abbildungen landeten beim automatischen Einfuegen im Codeblock (ihre
Anker standen in abgedruckten Ausgaben) und wurden dahinter verschoben; eine
Pruefung ueber alle elf zeigt null Fehlplatzierungen.

Neu: bilder_04/stil_04.py buendelt Palette, rcParams und speichere().
networkx wurde entgegen der Planung nicht gebraucht - pyproject.toml bleibt
unveraendert. PDF von 781 auf 788 Seiten, 44 Diagramme, 27 Generatoren.
Veroeffentlicht: 25 Dateien.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 01:11:33 +02:00

277 lines
13 KiB
Markdown

# Optimierte Entscheidungsfindung mit Python
*Von der mathematischen Modellierung zu praktischen intelligenten Lösungen für Betrieb,
Technik und Finanzmärkte*
Ein Lehrbuch über Operations Research mit Python: **23 Kapitel in 5 Teilen, 6 Anhänge,
141 Aufgaben mit vollständigen Lösungen, 76 lauffähige Beispielprogramme, 25 Jupyter-Notebooks,
44 Diagramme — 788 PDF-Seiten.** Von der linearen Programmierung über Constraint Programming
und Metaheuristiken bis zu Portfoliooptimierung, CVaR und dem Weg vom Prototyp in den Betrieb.
**Online lesen: <https://jamulix.de/OR/>** · Repository: <https://kitux.de/forgejo/dschlueter/operations_research>
Autor: **Dieter Schlüter** · Lizenz: MIT (Code) und CC BY-SA 4.0 (Text)
> **Der Grundsatz des Buchs:** Jede abgedruckte Ausgabe stammt aus einem echten Lauf des
> danebenstehenden Programms, jede Zahl im Text ist gerechnet und nicht geschätzt. Allein
> beim Nachbauen von zehn Diagrammen kamen so sieben Fehler ans Licht, die beim Hinsehen
> niemand bemerkt hatte.
---
## Das Buch lesen
| Format | Wo |
| --- | --- |
| **Website** — eine Seite je Kapitel, mit Volltextsuche, Stichwortverzeichnis und vier interaktiven Plotly-Figuren | **<https://jamulix.de/OR/>** — oder lokal `OR_HTML_04/index.html` |
| **PDF**, 759 Seiten | <https://jamulix.de/OR/Operations_Research_mit_Python_Version_04.pdf> — oder lokal [`…_Version_04.pdf`](Operations_Research_mit_Python_Version_04.pdf) |
| **Ein einziges HTML-Dokument** | <https://jamulix.de/OR/gesamtdokument.html> |
| **Notebooks**, eines je Kapitel, einzeln oder als ZIP | <https://jamulix.de/OR/notebooks.html> — oder lokal `Notebooks_04/` |
Die Website ist selbstgenügsam — alle Verweise zeigen auf Unterverzeichnisse (`assets/`,
`bilder_04/`, `katex/`, `Notebooks_04/`). Der Ordner lässt sich unverändert auf einen
Webserver kopieren, ohne Konfiguration.
**Wegweiser durch den Inhalt** — Aufbau, Lernpfade, Voraussetzungen:
[`Operations_Research_mit_Python_Version_04/README.md`](Operations_Research_mit_Python_Version_04/README.md).
Vier rollenbezogene Einstiege (Entscheider, Ingenieure, Investoren, Studierende) stehen im
Vorwort des Buchs.
---
## Die Beispiele ausführen — drei Wege
### 1. Im Container, ohne etwas zu installieren
Der empfohlene Weg zum Ausprobieren. Das Kurs-Image bringt alle Solver in geprüften
Versionen mit.
```bash
docker build -t or-mit-python .
# JupyterLab mit allen 25 Kapitel-Notebooks -> http://127.0.0.1:8888
docker run --rm -p 127.0.0.1:8888:8888 or-mit-python
# oder ein einzelnes Programm
docker run --rm or-mit-python Installationstest.py
docker run --rm or-mit-python Rucksack.py
# Ergebnisdateien behalten (Excel-Mappen, Diagramme)
docker run --rm -v "$PWD/ausgabe:/buch/output" or-mit-python Excel_Bruecke.py
```
JupyterLab läuft im Image **ohne Zugangstoken** — für den eigenen Rechner richtig, und
deshalb steht oben `127.0.0.1:8888:8888` statt `8888:8888`: So ist der Dienst nur lokal
erreichbar. Stellen Sie den Container nicht ins offene Netz.
Das Image führt die Programme aus, **es baut das Buch nicht**: pandoc, xelatex und inkscape
wären zusammen über ein Gigabyte ohne Nutzen für jemanden, der rechnen will.
### 2. Schlanke lokale Installation
Über [`pyproject.toml`](pyproject.toml) und seine Gruppen — die Grundausstattung trägt den
Großteil des Buchs:
```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -e . # numpy, scipy, pandas, matplotlib, openpyxl, pydantic, ortools, cvxpy
pip install -e ".[finance]" # scikit-learn, yfinance — Teil IV
pip install -e ".[large-scale]" # highspy, Pyomo, linopy, polars
pip install -e ".[api]" # fastapi, uvicorn, httpx — Kapitel Testen
pip install -e ".[notebook]" # jupyterlab
pip install -e ".[figures]" # plotly, graphviz — nur zum Neuerzeugen der Diagramme
pip install -e ".[dev]" # pytest
```
**`ortools` steht in der Grundausstattung, `highspy` erst in `[large-scale]`.** Das ist
Absicht: Beide bringen eine eigene HiGHS-Kopie mit und vertragen sich nicht im selben
Prozess. Wer nur die Grundausstattung installiert, kann in den Konflikt gar nicht geraten.
Die drei Programme, die beide brauchen, isolieren sie zur Laufzeit in getrennten Prozessen —
`ProcessPoolExecutor` mit `spawn` und `max_tasks_per_child=1`, erklärt im Kapitel „Das
Python-Ökosystem für OR".
### 3. Alles auf einen Schlag
So wie im Buch abgedruckt, über
[`requirements.txt`](Operations_Research_mit_Python_Version_04_Programme/requirements.txt):
```bash
pip install -r Operations_Research_mit_Python_Version_04_Programme/requirements.txt
```
> **Prüfen Sie die Installation, bevor Sie anfangen:**
> `python3 Operations_Research_mit_Python_Version_04_Programme/Installationstest.py`
> meldet für jedes Paket Version und Status und löst je Solver-Familie ein Mini-Modell.
**Vier der 76 Programme brauchen Internet** (Kursdaten über `yfinance`):
`CVaR_Portfolio.py`, `Finanzdaten_Ledoit_Wolf.py`, `Markowitz_CVXPY.py`,
`QuantitativeTradingEngine.py`. Alle übrigen laufen offline.
---
## Was hier liegt
| Pfad | Rolle |
| --- | --- |
| `Operations_Research_mit_Python_Version_04/` | **Quelle**: 37 Kapiteldateien (inkl. 5 Teil-Synthesen), Build-Skripte und `NEUER_TITEL.md` — der Titeltext, aus dem PDF-Titelseite und Startseite entstehen |
| `bilder_04/` | **Quelle**: 44 Diagramme (SVG) + 27 `erzeuge_*.py`-Generatoren |
| `web_04/` | **Quelle**: `site.css`, `site.js`, `icons.svg`, `plotly.min.js`, `katex/` — die statischen Bestandteile der Website |
| `pyproject.toml`, `Dockerfile`, `.dockerignore` | **Quelle**: Abhängigkeitsgruppen und Kurs-Image |
| `pandoc-defaults-*.yaml`, `pandoc/` | **Quelle**: Konfiguration des PDF-Baus |
| `PLAN.md` / `PROGRESS.md` | **Quelle**: Ziel des Umbaus und erreichter Stand |
| `Operations_Research_mit_Python_Version_04.md` | generiert: Gesamtdokument (Pandoc-Eingabe) |
| `Operations_Research_mit_Python_Version_04.pdf` | generiert: PDF, 759 Seiten |
| `OR_HTML_04/` | generiert: **Mehrseiten-Website** — dieser Ordner wird veröffentlicht |
| `Operations_Research_mit_Python_Version_04_Programme/` | generiert: 76 Beispielprogramme |
| `Notebooks_04/` | generiert: ein Jupyter-Notebook je Kapitel |
**Nur die als *Quelle* markierten Verzeichnisse werden von Hand bearbeitet.** Alles andere
wird erzeugt und bei jeder inhaltlichen Änderung neu gebaut. Insbesondere gehören
**Programmänderungen in den Kapitel-Codeblock**, nicht in das Programme-Verzeichnis — sonst
gehen sie beim nächsten Bau verloren.
---
## Das Buch selbst bauen
Alle Befehle werden **in diesem Verzeichnis** ausgeführt; die Skripte leiten ihre Pfade aus
der eigenen Position ab.
```bash
# nur prüfen: Struktur, Querverweise, Codezäune, harte Nummern, Lesekette
python3 Operations_Research_mit_Python_Version_04/build_version_04.py --check
# Gesamtdokument, PDF und Website
python3 Operations_Research_mit_Python_Version_04/build_version_04.py --pdf --html
# Beispielprogramme aus den Kapiteln extrahieren
python3 Operations_Research_mit_Python_Version_04/extract_programme_04.py
# Glossar (Anhang E) aus seiner Eintragsliste erzeugen
python3 Operations_Research_mit_Python_Version_04/erzeuge_glossar_04.py
# ein Diagramm neu erzeugen (braucht die Gruppe [figures], teils 'dot')
python3 bilder_04/erzeuge_kondition.py
```
`--check` prüft mehr als die Struktur: fehlende Codezäune, tote Dateilinks, eine gebrochene
Lesekette, Lösungsabschnitte mit falscher Aufgabenzahl — und **jede von Hand geschriebene
Nummer**, die der Build selbst vergeben müsste. Diese Regel ist die in diesem Projekt am
häufigsten verletzte; zehn Fälle wurden nachträglich gefunden, und jeder hat eine eigene
Prüfung hinterlassen.
### Externe Werkzeuge
| Werkzeug | Wofür | Ohne es |
| --- | --- | --- |
| `pandoc` (≥ 3.0) | PDF und Website | `--pdf`/`--html` scheitern; `--check` läuft |
| `xelatex` + `texindy` | PDF | kein PDF; Website unberührt |
| `inkscape` | SVG-Grafiken im PDF | LaTeX bricht bei `\includesvg` ab |
| `dot` (Graphviz) | 5 der 16 Bildgeneratoren | nur beim Neuerzeugen dieser Diagramme nötig |
**`web_04/katex/`** ist externes Material und wird von keinem Skript erzeugt — es liegt
deshalb als Quelle im Repository und wird bei jedem Bau nach `OR_HTML_04/katex/` kopiert.
Fehlt es, bleiben die Formeln auf der Website ungesetzt.
**Warum die PDF-Konfiguration doppelt vorliegt:** `pandoc-defaults-basis.yaml` ist eine Kopie
der Benutzerdatei `~/.config/pandoc/defaults.yaml` — mit **relativem** Pfad auf
`pandoc/header-includes.tex` statt des absoluten, der dort steht. Ohne diese Kopie könnte ein
frischer Klon kein PDF bauen. `build_version_04.py` bevorzugt sie und fällt auf die
Benutzerdatei zurück, falls sie fehlt.
---
## Veröffentlichen
`OR_HTML_04/` ist selbstgenügsam: Der Ordner lässt sich direkt auf einen Webserver kopieren.
Für den regelmäßigen Fall gibt es ein Skript.
```bash
# einmalig: den eigenen SSH-Schlüssel auf den Server legen
ssh-copy-id benutzer@example.org
# einmalig: Ziel eintragen
cp .env.beispiel .env && chmod 600 .env # dann .env ausfüllen
# jedes Mal
python3 Operations_Research_mit_Python_Version_04/veroeffentliche_04.py
```
Das Skript benutzt **`rsync --delete`, nicht `scp`** — und das ist der entscheidende
Unterschied: `scp` überschreibt nur, es entfernt nie. Eine Seite, die aus dem Buch
verschwindet, bliebe sonst dauerhaft online erreichbar. `rsync` überträgt zudem nur, was sich
geändert hat; ein zweiter Lauf direkt danach überträgt nichts.
Vier Schritte, jeder mit einer eigenen Aufgabe:
1. **Vorprüfung.** Ist eine Quelldatei neuer als die gebaute Website, bricht das Skript ab —
ein alter Stand soll gar nicht erst hochgeladen werden. Ebenso geprüft: dass das PDF der
Website byte-genau dem gebauten entspricht und dass die Verzeichnis-Weiterleitungen da
sind.
2. **Probelauf.** Zählt und zeigt, was übertragen und was gelöscht würde. Über 30 Löschungen
ist der Lauf verdächtig (falscher Zielpfad, halb gebautes Verzeichnis) und bricht ab;
`--loeschgrenze N` hebt die Grenze an.
3. **Übertragung.**
4. **Nachprüfung per HTTP.** Dreizehn Adressen werden abgerufen und ihr Statuscode mit der
Erwartung verglichen.
`--probelauf` führt nur die Schritte 1 und 2 aus. In der `.env` steht **kein Geheimnis**:
`rsync` und `scp` haben keine Passwort-Option, die Anmeldung läuft über den SSH-Schlüssel.
---
## Notebooks und Google Colab
Jedes Kapitel hat ein Notebook in `Notebooks_04/`, und die Website verlinkt es zum
Herunterladen. Einen **Colab-Knopf gibt es bewusst nicht**: Colab öffnet Notebooks nur aus
GitHub, aus Google Drive oder aus einem Upload — eine selbstgehostete Git-Instanz kann es
nicht lesen. Wer ohne eigene Installation rechnen will, nimmt das Kurs-Image (Weg 1); wer
Colab bevorzugt, lädt die `.ipynb` herunter und dort hoch.
Sollte das Repository später zusätzlich auf GitHub gespiegelt werden, genügt **ein** Wert:
`COLAB_BASIS_URL` in `build_version_04.py`. Der Pfad dahinter stimmt bereits.
---
## Lizenz
Zwei Lizenzen, je nachdem, was Sie verwenden:
| Was | Lizenz | Datei |
| --- | --- | --- |
| **Programmcode** — alle `.py`, Notebooks, `Dockerfile`, `pyproject.toml` | MIT | [`LICENSE`](LICENSE) |
| **Buchtext und Abbildungen**`.md`-Kapitel, PDF, Website, Diagramme | CC BY-SA 4.0 | [`LICENSE-TEXT.md`](LICENSE-TEXT.md) |
Ein Codeblock, der im Buchtext abgedruckt ist, bleibt Programmcode: Für ihn gilt MIT.
Die verwendeten Bibliotheken (HiGHS, Google OR-Tools, CVXPY, SciPy, Pyomo, linopy und
andere) stehen unter ihren eigenen Lizenzen. Für die Inhalte gibt es **keine
Gewährleistung** — die Modelle sind Lehrbeispiele; wer auf ihrer Grundlage betriebliche oder
finanzielle Entscheidungen trifft, tut das in eigener Verantwortung.
---
## Mitwirken
Fehler, Unklarheiten und Verbesserungsvorschläge sind willkommen. Besonders hilfreich:
* **Eine Zahl, die nicht stimmt.** Jede abgedruckte Ausgabe soll aus einem echten Lauf
stammen — ergibt Ihrer etwas anderes, ist das ein Fund. Bitte mit Python- und
Paketversion.
* **Eine Stelle, an der das Buch etwas behauptet, statt es zu zeigen.**
* **Ein Beispiel aus Ihrem Arbeitsalltag**, das ein Muster besser erklärt als das im Buch.
Wer selbst etwas ändert: Programmänderungen gehören in den **Kapitel-Codeblock**, danach
`extract_programme_04.py` laufen lassen. Vor dem Absenden `--check`. Die Konventionen stehen
kurz in `CLAUDE.md` und ausführlich in `PLAN.md` Abschnitt 9.
---
## Verlauf
Version 04 baut das Werk vom Nachschlagewerk zum Kursbegleiter um. **Was geplant war, steht
in [`PLAN.md`](PLAN.md); was erreicht ist und woran man das nachprüft, in
[`PROGRESS.md`](PROGRESS.md)** — einschließlich aller Funde unterwegs und der Regeln, die
sich daraus ergeben haben. Wer hier weiterarbeitet, liest beide zuerst.
Bis Version 03 hieß das Buch *Operations Research mit Python*.