diff --git a/.dockerignore b/.dockerignore
index 6bf26d4..80a4f8b 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -2,9 +2,9 @@
# ganze Verzeichnis an den Daemon - mitsamt Website (rund 50 MB), PDF und
# Notebooks, die im Image nichts zu suchen haben.
-# Erzeugte Ausgaben
+# Erzeugte Ausgaben. Notebooks_04/ steht bewusst NICHT hier: Die 25 Notebooks
+# gehoeren ins Image, damit JupyterLab darin etwas zu oeffnen hat.
OR_HTML_04/
-Notebooks_04/
*.pdf
Operations_Research_mit_Python_Version_04.md
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..52ed8a1
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,50 @@
+# Zeilenenden. Ohne diese Datei entscheidet die core.autocrlf-Einstellung des
+# jeweiligen Rechners, was beim Klonen passiert - und ein Windows-Leser bekommt
+# je nach Konfiguration entweder CRLF-Rauschen in jedem Diff oder Python-
+# Dateien, die sein Editor als "gemischte Zeilenenden" anmeckert.
+#
+# Die Regel hier legt es fuer alle fest: Im Repository steht immer LF, im
+# Arbeitsverzeichnis bekommt jeder, was zu seinem System passt.
+
+* text=auto eol=lf
+
+# --- Quelltext und Buchtext: ausdruecklich Text ----------------------------
+*.py text diff=python
+*.md text
+*.txt text
+*.toml text
+*.yaml text
+*.yml text
+*.tex text
+*.css text
+*.js text
+*.html text
+*.svg text
+*.ipynb text
+Dockerfile text
+.gitignore text
+.dockerignore text
+.gitattributes text
+
+# --- Binaerdateien: niemals anfassen ---------------------------------------
+# Ein versehentliches Umschreiben der Zeilenenden macht sie unbrauchbar.
+*.pdf binary
+*.png binary
+*.jpg binary
+*.jpeg binary
+*.gif binary
+*.webp binary
+*.woff binary
+*.woff2 binary
+*.ttf binary
+*.otf binary
+*.xlsx binary
+*.zip binary
+
+# --- Was auf GitHub/Forgejo die Sprachstatistik verzerrt --------------------
+# Die Website und die Notebooks sind ERZEUGT. Ohne diese Markierung zaehlt
+# Linguist das Repository als ueberwiegend HTML - es ist ein Python-Projekt.
+OR_HTML_04/** linguist-generated=true
+Notebooks_04/** linguist-generated=true
+Operations_Research_mit_Python_Version_04_Programme/** linguist-generated=true
+Operations_Research_mit_Python_Version_04.md linguist-generated=true
diff --git a/CLAUDE.md b/CLAUDE.md
index dc46454..fc89464 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -33,7 +33,9 @@ Notebooks_04/ generiert: ein .ipynb je Kapitel
Kritik_und_Verbesserungsvorschlaege/ Quelle: Rezension, Verbesserungsvorschläge,
NEUER_TITEL.md (Vorlage des Titelblatts)
pyproject.toml Quelle: Abhängigkeiten in Gruppen
-Dockerfile, .dockerignore Quelle: Kurs-Image (nur Programme)
+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
pandoc-defaults-basis.yaml, pandoc/, pandoc-defaults-buch.yaml PDF-Konfiguration
```
@@ -154,6 +156,23 @@ Im PDF steht stattdessen ein Hinweis auf die Website.
---
+## Veroeffentlichung
+
+Das Repository liegt oeffentlich auf .
+
+**`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.
diff --git a/Dockerfile b/Dockerfile
index c19e596..48c75fb 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -9,8 +9,8 @@
# ist mit einer lokalen Installation besser bedient (siehe README.md).
#
# docker build -t or-mit-python .
-# docker run --rm or-mit-python Installationstest.py
-# docker run --rm -it or-mit-python # Python-Eingabe
+# docker run --rm -p 127.0.0.1:8888:8888 or-mit-python # JupyterLab
+# docker run --rm or-mit-python Installationstest.py # ein Programm
# docker run --rm -v "$PWD/ausgabe:/buch/output" or-mit-python Excel_Bruecke.py
#
# Zwei Stufen: Die erste baut die Abhaengigkeiten in eine virtuelle Umgebung
@@ -44,7 +44,7 @@ COPY pyproject.toml README.md ./
# Der Konflikt wird nicht durch Weglassen geloest, sondern zur Laufzeit durch
# getrennte Prozesse (Kapitel Oekosystem). Ein Image, das nur eines von beiden
# enthaelt, koennte die Haelfte der Beispiele nicht ausfuehren.
-RUN pip install ".[finance,large-scale,api,dev]"
+RUN pip install ".[finance,large-scale,api,dev,notebook]"
# --- Stufe 2: das eigentliche Image -----------------------------------------
FROM python:3.12-slim AS laufzeit
@@ -66,14 +66,30 @@ ENV PATH="/opt/venv/bin:$PATH" \
RUN useradd --create-home --uid 1000 kurs
WORKDIR /buch
COPY --chown=kurs:kurs Operations_Research_mit_Python_Version_04_Programme/ ./
+COPY --chown=kurs:kurs Notebooks_04/ ./Notebooks_04/
# Verzeichnis fuer erzeugte Dateien - als Volume einhaengbar, damit
# Excel-Ausgaben und Diagramme den Container ueberleben. Angelegt wird es
# VOR dem Benutzerwechsel, denn /buch gehoert root.
RUN mkdir -p /buch/output && chown kurs:kurs /buch/output
-USER kurs
-# Ohne Argument eine Python-Eingabeaufforderung, mit Argument das genannte
-# Programm: 'docker run --rm or-mit-python Rucksack.py'
-ENTRYPOINT ["python"]
+# Ein Startbefehl, zwei Betriebsarten - damit niemand '--entrypoint' braucht:
+# ohne Argument -> JupyterLab mit den 25 Kapitel-Notebooks
+# mit Argument -> genau dieses Programm
+RUN printf '%s\n' \
+ '#!/bin/sh' \
+ 'if [ $# -eq 0 ]; then' \
+ ' exec jupyter lab --ip=0.0.0.0 --port=8888 --no-browser \' \
+ ' --ServerApp.token= --ServerApp.root_dir=/buch' \
+ 'fi' \
+ 'exec python "$@"' \
+ > /usr/local/bin/starte && chmod +x /usr/local/bin/starte
+
+USER kurs
+EXPOSE 8888
+
+# JupyterLab laeuft ohne Token. Das ist fuer ein Kurs-Image auf dem eigenen
+# Rechner richtig - deshalb im README die Portfreigabe an 127.0.0.1 binden
+# und den Container NICHT ins offene Netz stellen.
+ENTRYPOINT ["/usr/local/bin/starte"]
CMD []
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4a1f951
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,39 @@
+Dieses Repository steht unter ZWEI Lizenzen. Welche gilt, hängt davon ab,
+was Sie verwenden möchten:
+
+ * PROGRAMMCODE — MIT-Lizenz (unten im Wortlaut).
+ Betrifft: alle .py-Dateien (Beispielprogramme, Build-Skripte,
+ Diagrammgeneratoren), die Jupyter-Notebooks, Dockerfile, pyproject.toml
+ und die Konfigurationsdateien.
+
+ * BUCHTEXT UND ABBILDUNGEN — Creative Commons BY-SA 4.0.
+ Betrifft: alle .md-Kapitelquellen, das erzeugte PDF, die Website in
+ OR_HTML_04/ und die Diagramme in bilder_04/.
+ Wortlaut und Bedingungen: siehe LICENSE-TEXT.md.
+
+Im Zweifel und für Mischformen (etwa ein Codeblock, der im Buchtext
+abgedruckt ist): Der Code darin steht unter der MIT-Lizenz.
+
+--------------------------------------------------------------------------
+
+MIT License
+
+Copyright (c) 2026 Dieter Schlüter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/LICENSE-TEXT.md b/LICENSE-TEXT.md
new file mode 100644
index 0000000..df4790e
--- /dev/null
+++ b/LICENSE-TEXT.md
@@ -0,0 +1,56 @@
+# Lizenz für Buchtext und Abbildungen
+
+**Optimierte Entscheidungsfindung mit Python** — © 2026 Dieter Schlüter
+
+Der Buchtext und die Abbildungen stehen unter der Lizenz
+**Creative Commons Namensnennung – Weitergabe unter gleichen Bedingungen 4.0
+International (CC BY-SA 4.0)**.
+
+* Kurzfassung (allgemeinverständlich):
+* Verbindlicher Volltext:
+
+## Was darunter fällt
+
+| Betroffen | Nicht betroffen |
+| --- | --- |
+| Die Kapitelquellen in `Operations_Research_mit_Python_Version_04/*.md` | Sämtlicher Programmcode (`*.py`, Notebooks, `Dockerfile`, `pyproject.toml`) — der steht unter der **MIT-Lizenz**, siehe `LICENSE` |
+| Das erzeugte PDF | |
+| Die Website in `OR_HTML_04/` | Die Schriftdateien unter `OR_HTML_04/assets/` und `katex/` — sie tragen ihre eigenen Lizenzen |
+| Die Diagramme in `bilder_04/` | |
+
+Ein Codeblock, der im Buchtext abgedruckt ist, bleibt Programmcode: Für ihn
+gilt die MIT-Lizenz.
+
+## Was Sie dürfen
+
+**Teilen** — das Material in jedem Format vervielfältigen und weiterverbreiten.
+**Bearbeiten** — es remixen, verändern und darauf aufbauen, auch kommerziell.
+
+## Unter diesen Bedingungen
+
+**Namensnennung.** Nennen Sie den Autor (Dieter Schlüter), verlinken Sie auf
+die Lizenz und geben Sie an, ob Änderungen vorgenommen wurden. Eine
+brauchbare Form:
+
+> Nach *Optimierte Entscheidungsfindung mit Python* von Dieter Schlüter
+> (), lizenziert unter
+> CC BY-SA 4.0. Bearbeitet: [was Sie geändert haben].
+
+**Weitergabe unter gleichen Bedingungen.** Wenn Sie das Material verändern
+oder darauf aufbauen, müssen Sie Ihre Beiträge unter derselben Lizenz wie das
+Original verbreiten.
+
+**Keine weiteren Einschränkungen.** Sie dürfen keine zusätzlichen Klauseln
+anwenden, die anderen rechtlich etwas untersagen, was die Lizenz erlaubt.
+
+## Was diese Lizenz nicht abdeckt
+
+Sie erstreckt sich nicht auf Marken- und Namensrechte Dritter. Die im Buch
+genannten Bibliotheken — HiGHS, Google OR-Tools, CVXPY, SciPy, Pyomo, linopy
+und andere — stehen unter ihren eigenen Lizenzen; wer sie einsetzt, hält sich
+an diese.
+
+Für die Inhalte gibt es **keine Gewährleistung**. Die Modelle und Zahlen sind
+Lehrbeispiele. Wer auf ihrer Grundlage betriebliche oder finanzielle
+Entscheidungen trifft, tut das in eigener Verantwortung — das gilt besonders
+für die Kapitel zu Finanzmärkten.
diff --git a/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf b/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf
index e86efe9..98f119d 100644
Binary files a/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf and b/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf differ
diff --git a/OR_HTML_04/bruecke.html b/OR_HTML_04/bruecke.html
index 9816a53..909a935 100644
--- a/OR_HTML_04/bruecke.html
+++ b/OR_HTML_04/bruecke.html
@@ -126,8 +126,7 @@
Zeitbedarf: ca. 2,5 Stunden.
Programme:
Strukturbruecke.py
-Notebook: bruecke.ipynb
-In Google Colab öffnen
+Notebook: bruecke.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/cpsat.html b/OR_HTML_04/cpsat.html
index 98c08c4..4f97ddf 100644
--- a/OR_HTML_04/cpsat.html
+++ b/OR_HTML_04/cpsat.html
@@ -114,8 +114,7 @@
Parallele_Suche.py
CP_SAT_Statusfaelle.py
Strafgewichte.py
-Notebook: cpsat.ipynb
-In Google Colab öffnen
+Notebook: cpsat.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/cvar.html b/OR_HTML_04/cvar.html
index cdae5e8..7843c7b 100644
--- a/OR_HTML_04/cvar.html
+++ b/OR_HTML_04/cvar.html
@@ -110,8 +110,7 @@
Programme:
VaR_CVaR_Demo.py
CVaR_Portfolio.py
-Notebook: cvar.ipynb
-In Google Colab öffnen
+Notebook: cvar.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/dekomposition.html b/OR_HTML_04/dekomposition.html
index a59870b..c5e59af 100644
--- a/OR_HTML_04/dekomposition.html
+++ b/OR_HTML_04/dekomposition.html
@@ -109,8 +109,7 @@
Zeitbedarf: ca. 3 Stunden.
Programme:
Spaltengenerierung.py
-Notebook: dekomposition.ipynb
-In Google Colab öffnen
+Notebook: dekomposition.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/dynamische-programmierung.html b/OR_HTML_04/dynamische-programmierung.html
index 59a34c0..61231b5 100644
--- a/OR_HTML_04/dynamische-programmierung.html
+++ b/OR_HTML_04/dynamische-programmierung.html
@@ -110,8 +110,7 @@
Programme:
Bellman_Minimalbeispiel.py
Mehrperiodige_Order_Execution.py
-Notebook: dynamische-programmierung.ipynb
-In Google Colab öffnen
+Notebook: dynamische-programmierung.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/einfuehrung.html b/OR_HTML_04/einfuehrung.html
index 77cab65..4e3583f 100644
--- a/OR_HTML_04/einfuehrung.html
+++ b/OR_HTML_04/einfuehrung.html
@@ -114,8 +114,7 @@
Brute_Force_Vergleich.py
Bausteine_Vorlage.py
Excel_Bruecke.py
-Notebook: einfuehrung.ipynb
-In Google Colab öffnen
+Notebook: einfuehrung.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/finanzdaten.html b/OR_HTML_04/finanzdaten.html
index 2645caa..15c0fed 100644
--- a/OR_HTML_04/finanzdaten.html
+++ b/OR_HTML_04/finanzdaten.html
@@ -113,8 +113,7 @@
Finanzdaten_Ledoit_Wolf.py
Kovarianz_Falle.py
⚠️ Überspringen Sie dieses Kapitel nicht. Wer direkt bei Markowitz (Kapitel 19) einsteigt, erhält absurde Portfoliogewichte und weiß nicht, warum.
-Notebook: finanzdaten.ipynb
-In Google Colab öffnen
+Notebook: finanzdaten.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/fundament.html b/OR_HTML_04/fundament.html
index 97bbb7c..a238092 100644
--- a/OR_HTML_04/fundament.html
+++ b/OR_HTML_04/fundament.html
@@ -112,8 +112,7 @@
Visualisierung_Loesungsraum.py
Konvexitaet_Demo.py
Skalierung_Kondition.py
-Notebook: fundament.ipynb
-In Google Colab öffnen
+Notebook: fundament.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/gesamtdokument.html b/OR_HTML_04/gesamtdokument.html
index 1773fb9..273ffb1 100644
--- a/OR_HTML_04/gesamtdokument.html
+++ b/OR_HTML_04/gesamtdokument.html
@@ -3114,8 +3114,7 @@ Alles bereit — Sie können mit dem ersten Kapitel beginnen.
Brute_Force_Vergleich.py
Bausteine_Vorlage.py
Excel_Bruecke.py
-Notebook: Notebooks_04/einfuehrung.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/einfuehrung.ipynb
1.1 In 5 Minuten gelöst
@@ -4421,8 +4420,7 @@ Deckungsbeitrag: 18600 EUR
Visualisierung_Loesungsraum.py
Konvexitaet_Demo.py
Skalierung_Kondition.py
-Notebook: Notebooks_04/fundament.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/fundament.ipynb
2.1 In 5 Minuten gelöst
@@ -5592,8 +5590,7 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
Solver_Wahl.py
Modellierungsschichten.py
Vektorisierte_Modellgenerierung.py
-Notebook: Notebooks_04/oekosystem.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/oekosystem.ipynb
3.1 In 5 Minuten gelöst
@@ -6933,8 +6930,7 @@ Groesse Variante Aufbau Loesen Aufba
Zeitbedarf: ca. 3 Stunden.
Programme:
Vom_Wunsch_zum_Modell.py
-Notebook: Notebooks_04/modellierung.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/modellierung.ipynb
4.1 In 5 Minuten gelöst
@@ -7889,8 +7885,7 @@ Lesart C (weich): loesbar - 66,000 EUR Deckungsbeitrag, 1 Stammauftrag abgelehnt
Sensitivitaetsanalyse.py
Dualitaet_Nachweis.py
Toleranzen_und_Entartung.py
-Notebook: Notebooks_04/lp.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/lp.ipynb
5.1 In 5 Minuten gelöst
@@ -9354,8 +9349,7 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
Solverstatus_und_Gap.py
Warmstart_Effekt.py
Big_M_Falle.py
-Notebook: Notebooks_04/milp.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/milp.ipynb
6.1 In 5 Minuten gelöst
@@ -11027,8 +11021,7 @@ funktioniert immer.
Parallele_Suche.py
CP_SAT_Statusfaelle.py
Strafgewichte.py
-Notebook: Notebooks_04/cpsat.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/cpsat.ipynb
7.1 In 5 Minuten gelöst
@@ -12730,8 +12723,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
Zuordnung_Ungarisch.py
VRP_Flotten_Routing.py
VRP_Kapazitaetsfalle.py
-Notebook: Notebooks_04/graphen.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/graphen.ipynb
8.1 In 5 Minuten gelöst
@@ -13674,8 +13666,7 @@ plausibel aussieht.
Simulated_Annealing.py
Metaheuristik_vs_Exakt.py
Large_Neighborhood_Search.py
-Notebook: Notebooks_04/metaheuristiken.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/metaheuristiken.ipynb
@@ -15245,8 +15236,7 @@ Abstand zum Optimum.
Zeitbedarf: ca. 3 Stunden.
Programme:
Spaltengenerierung.py
-Notebook: Notebooks_04/dekomposition.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/dekomposition.ipynb
10.1 In 5 Minuten gelöst
@@ -16093,8 +16083,7 @@ Mutterrolle 5,600 mm, 13 bestellte Breiten, 219 Zuschnitte.
KKT_Nachweis.py
Entropie_Maximierte_Allokation.py
Lokale_Optima_Multistart.py
-Notebook: Notebooks_04/qp-nlp.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/qp-nlp.ipynb
11.1 In 5 Minuten gelöst
@@ -17270,8 +17259,7 @@ The objective is not DCP. Its following subexpressions are not: QuadForm(...)Stochastische_Optimierung.py
Robuste_Optimierung.py
Chance_Constraints.py
-Notebook: Notebooks_04/unsicherheit.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/unsicherheit.ipynb
12.1 In 5 Minuten gelöst
@@ -18419,8 +18407,7 @@ Anteil ueber 11,34 Tage: 95.5 %
Programme:
Bellman_Minimalbeispiel.py
Mehrperiodige_Order_Execution.py
-Notebook: Notebooks_04/dynamische-programmierung.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/dynamische-programmierung.ipynb
13.1 In 5 Minuten gelöst
@@ -19288,8 +19275,7 @@ Abweichung DP zur analytischen Loesung: 5.788 % (Diskretisierung: 1000 Stueck)
Zeitbedarf: ca. 4 Stunden.
Programme:
Mehrziel_Pareto.py
-Notebook: Notebooks_04/mehrziel.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/mehrziel.ipynb
14.1 In 5 Minuten gelöst
@@ -19977,8 +19963,7 @@ e(x) \le \varepsilon
Zeitbedarf: ca. 3,5 Stunden.
Programme:
Predict_then_Optimize.py
-Notebook: Notebooks_04/prognose.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/prognose.ipynb
15.1 In 5 Minuten gelöst
@@ -20724,8 +20709,7 @@ Kosten statt auf Fehler trainiert.
Zeitbedarf: ca. 2,5 Stunden.
Programme:
Strukturbruecke.py
-Notebook: Notebooks_04/bruecke.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/bruecke.ipynb
16.1 In 5 Minuten gelöst
@@ -21395,8 +21379,7 @@ u_s \ge \ell_s^\top w - \gamma, \quad u_s \ge 0
Zeitbedarf: ca. 4 Stunden.
Programme:
Kraftwerkseinsatz.py
-Notebook: Notebooks_04/supplychain.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/supplychain.ipynb
17.1 In 5 Minuten gelöst
@@ -22179,8 +22162,7 @@ zweistufig 111111222222222222222111 (39 Blockstunden)
Finanzdaten_Ledoit_Wolf.py
Kovarianz_Falle.py
⚠️ Überspringen Sie dieses Kapitel nicht. Wer direkt bei Markowitz (Kapitel 19) einsteigt, erhält absurde Portfoliogewichte und weiß nicht, warum.
-Notebook: Notebooks_04/finanzdaten.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/finanzdaten.ipynb
18.1 In 5 Minuten gelöst
@@ -23032,8 +23014,7 @@ Ausfallraten und jede andere geschaetzte Groesse.
Diversifikation_Demo.py
Markowitz_CVXPY.py
Renditeschaetzung_Falle.py
-Notebook: Notebooks_04/markowitz.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/markowitz.ipynb
19.1 In 5 Minuten gelöst
@@ -24010,8 +23991,7 @@ Drei praktische Konsequenzen:
Programme:
VaR_CVaR_Demo.py
CVaR_Portfolio.py
-Notebook: Notebooks_04/cvar.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/cvar.ipynb
20.1 In 5 Minuten gelöst
@@ -24667,8 +24647,7 @@ CVaR: Portfolio 101.33 vs. Summe 159.24 -> subadditiv (kohaerent)
Backtest_Fallen.py
Data_Snooping.py
⚠️ Keine Anlageberatung. Dieses Kapitel demonstriert Methodik an realen Daten, keine handelbare Strategie. Abschnitt 21.7 erklärt ausführlich, warum ein guter Backtest noch lange keine funktionierende Strategie ist.
-Notebook: Notebooks_04/handelsmaschine.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/handelsmaschine.ipynb
21.1 In 5 Minuten gelöst
@@ -25728,8 +25707,7 @@ von allen Fehlversuchen genau einen: den erfolgreichsten.
Betriebsueberwachung.py
or_kern.py
Solverwechsel_CPSAT_HiGHS.py
-Notebook: Notebooks_04/praxisfallen.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/praxisfallen.ipynb
22.1 In 5 Minuten gelöst
@@ -28148,8 +28126,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
Mutationstest.py
Benchmark_Skalierung.py
Optimierungsdienst.py
-Notebook: Notebooks_04/testing.ipynb
-In Google Colab öffnen
+Notebook: Notebooks_04/testing.ipynb
23.1 In 5 Minuten gelöst
diff --git a/OR_HTML_04/graphen.html b/OR_HTML_04/graphen.html
index ecdbf41..a1acfef 100644
--- a/OR_HTML_04/graphen.html
+++ b/OR_HTML_04/graphen.html
@@ -112,8 +112,7 @@
Zuordnung_Ungarisch.py
VRP_Flotten_Routing.py
VRP_Kapazitaetsfalle.py
-Notebook: graphen.ipynb
-In Google Colab öffnen
+Notebook: graphen.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/handelsmaschine.html b/OR_HTML_04/handelsmaschine.html
index 221d285..3a70cb5 100644
--- a/OR_HTML_04/handelsmaschine.html
+++ b/OR_HTML_04/handelsmaschine.html
@@ -112,8 +112,7 @@
Backtest_Fallen.py
Data_Snooping.py
⚠️ Keine Anlageberatung. Dieses Kapitel demonstriert Methodik an realen Daten, keine handelbare Strategie. Abschnitt 21.7 erklärt ausführlich, warum ein guter Backtest noch lange keine funktionierende Strategie ist.
-Notebook: handelsmaschine.ipynb
-In Google Colab öffnen
+Notebook: handelsmaschine.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/lp.html b/OR_HTML_04/lp.html
index 11dda27..269eea6 100644
--- a/OR_HTML_04/lp.html
+++ b/OR_HTML_04/lp.html
@@ -124,8 +124,7 @@
Sensitivitaetsanalyse.py
Dualitaet_Nachweis.py
Toleranzen_und_Entartung.py
-Notebook: lp.ipynb
-In Google Colab öffnen
+Notebook: lp.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/markowitz.html b/OR_HTML_04/markowitz.html
index 6a937c3..c8180f4 100644
--- a/OR_HTML_04/markowitz.html
+++ b/OR_HTML_04/markowitz.html
@@ -111,8 +111,7 @@
Diversifikation_Demo.py
Markowitz_CVXPY.py
Renditeschaetzung_Falle.py
-Notebook: markowitz.ipynb
-In Google Colab öffnen
+Notebook: markowitz.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/mehrziel.html b/OR_HTML_04/mehrziel.html
index 0e7ca27..ab79eda 100644
--- a/OR_HTML_04/mehrziel.html
+++ b/OR_HTML_04/mehrziel.html
@@ -109,8 +109,7 @@
Zeitbedarf: ca. 4 Stunden.
Programme:
Mehrziel_Pareto.py
-Notebook: mehrziel.ipynb
-In Google Colab öffnen
+Notebook: mehrziel.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/metaheuristiken.html b/OR_HTML_04/metaheuristiken.html
index 580255f..0ce41b7 100644
--- a/OR_HTML_04/metaheuristiken.html
+++ b/OR_HTML_04/metaheuristiken.html
@@ -111,8 +111,7 @@
Simulated_Annealing.py
Metaheuristik_vs_Exakt.py
Large_Neighborhood_Search.py
-Notebook: metaheuristiken.ipynb
-In Google Colab öffnen
+Notebook: metaheuristiken.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/milp.html b/OR_HTML_04/milp.html
index 97f3d07..1b96f59 100644
--- a/OR_HTML_04/milp.html
+++ b/OR_HTML_04/milp.html
@@ -114,8 +114,7 @@
Solverstatus_und_Gap.py
Warmstart_Effekt.py
Big_M_Falle.py
-Notebook: milp.ipynb
-In Google Colab öffnen
+Notebook: milp.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/modellierung.html b/OR_HTML_04/modellierung.html
index 04c1a15..fa34a06 100644
--- a/OR_HTML_04/modellierung.html
+++ b/OR_HTML_04/modellierung.html
@@ -109,8 +109,7 @@
Zeitbedarf: ca. 3 Stunden.
Programme:
Vom_Wunsch_zum_Modell.py
-Notebook: modellierung.ipynb
-In Google Colab öffnen
+Notebook: modellierung.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/oekosystem.html b/OR_HTML_04/oekosystem.html
index f429a49..124f6d9 100644
--- a/OR_HTML_04/oekosystem.html
+++ b/OR_HTML_04/oekosystem.html
@@ -112,8 +112,7 @@
Solver_Wahl.py
Modellierungsschichten.py
Vektorisierte_Modellgenerierung.py
-Notebook: oekosystem.ipynb
-In Google Colab öffnen
+Notebook: oekosystem.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/praxisfallen.html b/OR_HTML_04/praxisfallen.html
index 0daf359..365d9f4 100644
--- a/OR_HTML_04/praxisfallen.html
+++ b/OR_HTML_04/praxisfallen.html
@@ -116,8 +116,7 @@
Betriebsueberwachung.py
or_kern.py
Solverwechsel_CPSAT_HiGHS.py
-Notebook: praxisfallen.ipynb
-In Google Colab öffnen
+Notebook: praxisfallen.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/prognose.html b/OR_HTML_04/prognose.html
index a6544da..57cfc73 100644
--- a/OR_HTML_04/prognose.html
+++ b/OR_HTML_04/prognose.html
@@ -109,8 +109,7 @@
Zeitbedarf: ca. 3,5 Stunden.
Programme:
Predict_then_Optimize.py
-Notebook: prognose.ipynb
-In Google Colab öffnen
+Notebook: prognose.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/qp-nlp.html b/OR_HTML_04/qp-nlp.html
index 8a4c527..6c55b48 100644
--- a/OR_HTML_04/qp-nlp.html
+++ b/OR_HTML_04/qp-nlp.html
@@ -125,8 +125,7 @@
KKT_Nachweis.py
Entropie_Maximierte_Allokation.py
Lokale_Optima_Multistart.py
-Notebook: qp-nlp.ipynb
-In Google Colab öffnen
+Notebook: qp-nlp.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/supplychain.html b/OR_HTML_04/supplychain.html
index c6b3550..2cab71e 100644
--- a/OR_HTML_04/supplychain.html
+++ b/OR_HTML_04/supplychain.html
@@ -109,8 +109,7 @@
Zeitbedarf: ca. 4 Stunden.
Programme:
Kraftwerkseinsatz.py
-Notebook: supplychain.ipynb
-In Google Colab öffnen
+Notebook: supplychain.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/testing.html b/OR_HTML_04/testing.html
index 34fdede..429280a 100644
--- a/OR_HTML_04/testing.html
+++ b/OR_HTML_04/testing.html
@@ -112,8 +112,7 @@
Mutationstest.py
Benchmark_Skalierung.py
Optimierungsdienst.py
-Notebook: testing.ipynb
-In Google Colab öffnen
+Notebook: testing.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/OR_HTML_04/unsicherheit.html b/OR_HTML_04/unsicherheit.html
index b57f638..bafb6d2 100644
--- a/OR_HTML_04/unsicherheit.html
+++ b/OR_HTML_04/unsicherheit.html
@@ -113,8 +113,7 @@
Stochastische_Optimierung.py
Robuste_Optimierung.py
Chance_Constraints.py
-Notebook: unsicherheit.ipynb
-In Google Colab öffnen
+Notebook: unsicherheit.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten
diff --git a/Operations_Research_mit_Python_Version_04.md b/Operations_Research_mit_Python_Version_04.md
index 6a4eb22..877b58d 100644
--- a/Operations_Research_mit_Python_Version_04.md
+++ b/Operations_Research_mit_Python_Version_04.md
@@ -738,8 +738,7 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
> `Bausteine_Vorlage.py`\
> `Excel_Bruecke.py`
>
-> **Notebook:** `Notebooks_04/einfuehrung.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/einfuehrung.ipynb)
+> **Notebook:** `Notebooks_04/einfuehrung.ipynb`
---
@@ -1767,8 +1766,7 @@ Ein Programm, das abstürzt, ist harmlos — Sie sehen sofort, dass etwas nicht
> `Konvexitaet_Demo.py`\
> `Skalierung_Kondition.py`
>
-> **Notebook:** `Notebooks_04/fundament.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/fundament.ipynb)
+> **Notebook:** `Notebooks_04/fundament.ipynb`
---
@@ -2891,8 +2889,7 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
> `Modellierungsschichten.py`\
> `Vektorisierte_Modellgenerierung.py`
>
-> **Notebook:** `Notebooks_04/oekosystem.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/oekosystem.ipynb)
+> **Notebook:** `Notebooks_04/oekosystem.ipynb`
---
@@ -4025,8 +4022,7 @@ Groesse Variante Aufbau Loesen Aufba
> **Programme:**\
> `Vom_Wunsch_zum_Modell.py`
>
-> **Notebook:** `Notebooks_04/modellierung.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/modellierung.ipynb)
+> **Notebook:** `Notebooks_04/modellierung.ipynb`
---
@@ -4758,8 +4754,7 @@ Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare Zielfunktion, un
> `Dualitaet_Nachweis.py`\
> `Toleranzen_und_Entartung.py`
>
-> **Notebook:** `Notebooks_04/lp.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/lp.ipynb)
+> **Notebook:** `Notebooks_04/lp.ipynb`
---
@@ -5958,8 +5953,7 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
> `Warmstart_Effekt.py`\
> `Big_M_Falle.py`
>
-> **Notebook:** `Notebooks_04/milp.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/milp.ipynb)
+> **Notebook:** `Notebooks_04/milp.ipynb`
---
@@ -7570,8 +7564,7 @@ funktioniert immer.
> `CP_SAT_Statusfaelle.py`\
> `Strafgewichte.py`
>
-> **Notebook:** `Notebooks_04/cpsat.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/cpsat.ipynb)
+> **Notebook:** `Notebooks_04/cpsat.ipynb`
---
@@ -9183,8 +9176,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
> `VRP_Flotten_Routing.py`\
> `VRP_Kapazitaetsfalle.py`
>
-> **Notebook:** `Notebooks_04/graphen.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/graphen.ipynb)
+> **Notebook:** `Notebooks_04/graphen.ipynb`
---
@@ -10115,8 +10107,7 @@ plausibel aussieht.
> `Metaheuristik_vs_Exakt.py`\
> `Large_Neighborhood_Search.py`
>
-> **Notebook:** `Notebooks_04/metaheuristiken.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/metaheuristiken.ipynb)
+> **Notebook:** `Notebooks_04/metaheuristiken.ipynb`
---
@@ -11490,8 +11481,7 @@ Genau deshalb bekommt sie ein eigenes Kapitel: [Kapitel 10](#kap-dekomposition).
> **Programme:**\
> `Spaltengenerierung.py`
>
-> **Notebook:** `Notebooks_04/dekomposition.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/dekomposition.ipynb)
+> **Notebook:** `Notebooks_04/dekomposition.ipynb`
---
@@ -12217,8 +12207,7 @@ Zwei weitere Kapitel schließen sich an, weil sie dieselbe Voraussetzung aufgebe
> `Entropie_Maximierte_Allokation.py`\
> `Lokale_Optima_Multistart.py`
>
-> **Notebook:** `Notebooks_04/qp-nlp.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/qp-nlp.ipynb)
+> **Notebook:** `Notebooks_04/qp-nlp.ipynb`
---
@@ -13336,8 +13325,7 @@ else:
> `Robuste_Optimierung.py`\
> `Chance_Constraints.py`
>
-> **Notebook:** `Notebooks_04/unsicherheit.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/unsicherheit.ipynb)
+> **Notebook:** `Notebooks_04/unsicherheit.ipynb`
---
@@ -14520,8 +14508,7 @@ Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er ko
> `Bellman_Minimalbeispiel.py`\
> `Mehrperiodige_Order_Execution.py`
>
-> **Notebook:** `Notebooks_04/dynamische-programmierung.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/dynamische-programmierung.ipynb)
+> **Notebook:** `Notebooks_04/dynamische-programmierung.ipynb`
---
@@ -15237,8 +15224,7 @@ Bei dynamischer Programmierung entscheidet eine einzige Frage über Erfolg oder
> **Programme:**\
> `Mehrziel_Pareto.py`
>
-> **Notebook:** `Notebooks_04/mehrziel.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/mehrziel.ipynb)
+> **Notebook:** `Notebooks_04/mehrziel.ipynb`
---
@@ -15904,8 +15890,7 @@ Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem in
> **Programme:**\
> `Predict_then_Optimize.py`
>
-> **Notebook:** `Notebooks_04/prognose.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/prognose.ipynb)
+> **Notebook:** `Notebooks_04/prognose.ipynb`
---
@@ -16561,8 +16546,7 @@ Er hat drei Abschnitte, und Sie können nach jedem aufhören:
> **Programme:**\
> `Strukturbruecke.py`
>
-> **Notebook:** `Notebooks_04/bruecke.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/bruecke.ipynb)
+> **Notebook:** `Notebooks_04/bruecke.ipynb`
---
@@ -17177,8 +17161,7 @@ Eine Analogie ist nur so nützlich wie die Grenzen, die man ihr zieht. Drei Ding
> **Programme:**\
> `Kraftwerkseinsatz.py`
>
-> **Notebook:** `Notebooks_04/supplychain.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/supplychain.ipynb)
+> **Notebook:** `Notebooks_04/supplychain.ipynb`
---
@@ -17875,8 +17858,7 @@ Das ist keine akademische Feinheit, sondern die reale Lage der Energiewirtschaft
>
> **⚠️ Überspringen Sie dieses Kapitel nicht.** Wer direkt bei Markowitz ([Kapitel 19](#kap-markowitz)) einsteigt, erhält absurde Portfoliogewichte und weiß nicht, warum.
>
-> **Notebook:** `Notebooks_04/finanzdaten.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/finanzdaten.ipynb)
+> **Notebook:** `Notebooks_04/finanzdaten.ipynb`
---
@@ -18690,8 +18672,7 @@ e 0: \mathbf{w}^ op\mathbf{S}\mathbf{w} = 0$ | „In genau diesen Richtungen mis
> `Markowitz_CVXPY.py`\
> `Renditeschaetzung_Falle.py`
>
-> **Notebook:** `Notebooks_04/markowitz.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/markowitz.ipynb)
+> **Notebook:** `Notebooks_04/markowitz.ipynb`
---
@@ -19585,8 +19566,7 @@ Drei praktische Konsequenzen:
> `VaR_CVaR_Demo.py`\
> `CVaR_Portfolio.py`
>
-> **Notebook:** `Notebooks_04/cvar.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/cvar.ipynb)
+> **Notebook:** `Notebooks_04/cvar.ipynb`
---
@@ -20251,8 +20231,7 @@ if __name__ == "__main__":
>
> **⚠️ Keine Anlageberatung.** Dieses Kapitel demonstriert Methodik an realen Daten, keine handelbare Strategie. [Abschnitt 21.7](#sec:handelsmaschine-vom-backtest-zum-betrieb) erklärt ausführlich, warum ein guter Backtest noch lange keine funktionierende Strategie ist.
>
-> **Notebook:** `Notebooks_04/handelsmaschine.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/handelsmaschine.ipynb)
+> **Notebook:** `Notebooks_04/handelsmaschine.ipynb`
---
@@ -21204,8 +21183,7 @@ Sechs Kapitel, zwei Branchen, die sich für unvergleichbar halten. Die Werkstatt
> `or_kern.py`\
> `Solverwechsel_CPSAT_HiGHS.py`
>
-> **Notebook:** `Notebooks_04/praxisfallen.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/praxisfallen.ipynb)
+> **Notebook:** `Notebooks_04/praxisfallen.ipynb`
---
@@ -23482,8 +23460,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
> `Benchmark_Skalierung.py`\
> `Optimierungsdienst.py`
>
-> **Notebook:** `Notebooks_04/testing.ipynb`\
-> [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/testing.ipynb)
+> **Notebook:** `Notebooks_04/testing.ipynb`
---
diff --git a/Operations_Research_mit_Python_Version_04.pdf b/Operations_Research_mit_Python_Version_04.pdf
index e86efe9..98f119d 100644
Binary files a/Operations_Research_mit_Python_Version_04.pdf and b/Operations_Research_mit_Python_Version_04.pdf differ
diff --git a/Operations_Research_mit_Python_Version_04/build_version_04.py b/Operations_Research_mit_Python_Version_04/build_version_04.py
index dcae003..289bf28 100644
--- a/Operations_Research_mit_Python_Version_04/build_version_04.py
+++ b/Operations_Research_mit_Python_Version_04/build_version_04.py
@@ -79,13 +79,22 @@ NOTEBOOK_VERZ = os.path.join(BASIS, NOTEBOOK_NAME)
# Leerstring => es werden keine Colab-Badges erzeugt, nur lokale
# Notebook-Hinweise.
#
-# ACHTUNG BEIM VEROEFFENTLICHEN: Der Pfad danach lautet 'Notebooks_04/.ipynb'
-# und wird relativ zur REPOSITORY-WURZEL aufgeloest. Seit der Trennung von
-# Version 03 und 04 ist das Version_04/ - der Pfad stimmt also, sobald dieses
-# Verzeichnis die Wurzel des neuen Repositorys ist. Zu aendern sind nur
-# Kontoname und Repository-Name in der URL unten. Die 25 Badges auf den
-# Kapitelseiten haengen daran; ein falscher Wert faellt erst beim Klicken auf.
-COLAB_BASIS_URL = "https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main"
+# WARUM HIER EIN LEERSTRING STEHT: Das Repository liegt auf einer eigenen
+# Forgejo-Instanz, und Colab kann Notebooks NUR aus GitHub, aus Google Drive
+# oder aus einem Upload oeffnen. Die URL-Form
+# 'colab.research.google.com/github///...' ist fest auf GitHub
+# verdrahtet - eine selbstgehostete Adresse dort einzusetzen ergibt keinen
+# Link zum eigenen Server, sondern einen toten GitHub-Link.
+#
+# Verloren geht dadurch nichts: Die 25 Notebooks liegen neben der Website in
+# OR_HTML_04/Notebooks_04/ und bekommen einen echten Download-Link. Wer sie
+# ohne eigene Installation ausfuehren will, nimmt das Kurs-Image
+# (docker run -p 8888:8888 or-mit-python) - siehe README.md.
+#
+# Wer spaeter doch nach GitHub spiegelt, traegt hier die Basis-URL ein; der
+# Pfad danach lautet 'Notebooks_04/.ipynb' und wird relativ zur
+# Repository-Wurzel aufgeloest, stimmt also unveraendert.
+COLAB_BASIS_URL = ""
# Reihenfolge der Kapitel im Gesamtdokument
DATEIEN = [
@@ -1885,6 +1894,10 @@ def ergaenze_notebook_hinweis(text: str, notebook: str | None,
lokal = (f"[{notebook}]({NOTEBOOK_NAME}/{notebook})" if als_link
else f"`{NOTEBOOK_NAME}/{notebook}`")
zeilen = [f"> **Notebook:** {lokal}"]
+ if als_link and not COLAB_BASIS_URL:
+ # Ohne Colab-Knopf soll wenigstens dastehen, was man mit der Datei tut.
+ zeilen[0] += (" \u2014 herunterladen und in Jupyter \u00f6ffnen, in Colab "
+ "hochladen oder mit dem Kurs-Image starten")
if COLAB_BASIS_URL:
ziel = f"{COLAB_BASIS_URL}/{NOTEBOOK_NAME}/{notebook}"
zeilen.append(f"> [In Google Colab öffnen]({ziel})")
diff --git a/PROGRESS.md b/PROGRESS.md
index c76910c..fd645e3 100644
--- a/PROGRESS.md
+++ b/PROGRESS.md
@@ -1345,13 +1345,20 @@ Teilhistorie, die vollständig aussieht und es nicht ist. Volle Historie ginge n
### Was von außen kommen muss
-1. **Remote-Repository anlegen und pushen.**
-2. **Danach `COLAB_BASIS_URL`** in `build_version_04.py` (Zeile 88) auf Kontoname und
- Repository-Namen setzen. Der Wert dort ist ein **Platzhalter**
- (`dschlueter/or-mit-python`); die 25 Colab-Badges hängen daran, und ein falscher Wert
- fällt erst beim Klicken auf. Danach `--html` neu bauen.
-3. **Veröffentlichen.** `OR_HTML_04/` ist selbstgenügsam und direkt auf einen Webserver
- kopierbar.
+1. ✅ **Remote-Repository angelegt und gepusht** — ,
+ öffentlich, Branch `main`.
+2. ✅ **`COLAB_BASIS_URL` geklärt — und zwar anders als geplant.** Der Platzhalter zeigte auf
+ GitHub, das Repository liegt auf einer eigenen Forgejo-Instanz. Colab öffnet Notebooks
+ **nur** aus GitHub, Google Drive oder einem Upload; die URL-Form
+ `colab.research.google.com/github/…` ist fest auf GitHub verdrahtet. Eine
+ selbstgehostete Adresse dort einzusetzen ergäbe keinen Link zum eigenen Server, sondern
+ einen toten GitHub-Link — der Kommentar im Quelltext („zu ändern sind nur Kontoname und
+ Repository-Name") ging von GitHub aus und war damit selbst irreführend.
+ Die Konstante steht jetzt auf `""`: Die Colab-Zeile entfällt, der **Download-Link auf das
+ Notebook bleibt**, ergänzt um den Hinweis, was man damit tut. Ein späterer GitHub-Spiegel
+ braucht weiterhin nur diesen einen Wert.
+3. ⏳ **Veröffentlichen.** `OR_HTML_04/` ist selbstgenügsam und direkt auf einen Webserver
+ kopierbar. Sobald die Adresse feststeht, gehört sie ins README.
### Bewusst zurückgestellt (aus `Verbesserungen_02/_03`)
diff --git a/README.md b/README.md
index d4a0158..24e68d9 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,108 @@
-# Optimierte Entscheidungsfindung mit Python — Version 04
+# Optimierte Entscheidungsfindung mit Python
-Quellen, Build-Werkzeuge und Ausgaben des Lehrbuchs *Optimierte Entscheidungsfindung mit Python*
-(Autor: Dieter Schlüter). Dieses Verzeichnis ist die Wurzel: Alle Skripte leiten ihre Pfade
-daraus ab, und alle Befehle unten werden **hier** ausgeführt.
+*Von der mathematischen Modellierung zu praktischen intelligenten Lösungen für Betrieb,
+Technik und Finanzmärkte*
-> Dies ist die Bau-Anleitung für das Repository. Der Wegweiser **für Leserinnen und Leser des
-> Buchs** — Aufbau, Lernpfade, Voraussetzungen — steht in
-> [`Operations_Research_mit_Python_Version_04/README.md`](Operations_Research_mit_Python_Version_04/README.md).
+Ein Lehrbuch über Operations Research mit Python: **23 Kapitel in 5 Teilen, 5 Anhänge,
+141 Aufgaben mit vollständigen Lösungen, 76 lauffähige Beispielprogramme, 25 Jupyter-Notebooks,
+33 Diagramme — 760 PDF-Seiten.** Von der linearen Programmierung über Constraint Programming
+und Metaheuristiken bis zu Portfoliooptimierung, CVaR und dem Weg vom Prototyp in den Betrieb.
+
+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 |
+| --- | --- |
+| **PDF**, 760 Seiten | [`Operations_Research_mit_Python_Version_04.pdf`](Operations_Research_mit_Python_Version_04.pdf) |
+| **Website** — eine Seite je Kapitel, mit Volltextsuche, Stichwortverzeichnis und vier interaktiven Plotly-Figuren | `OR_HTML_04/index.html` im Browser öffnen |
+| **Ein einziges HTML-Dokument** | `OR_HTML_04/gesamtdokument.html` |
+
+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.
---
@@ -15,29 +111,31 @@ daraus ab, und alle Befehle unten werden **hier** ausgeführt.
| Pfad | Rolle |
| --- | --- |
| `Operations_Research_mit_Python_Version_04/` | **Quelle**: 36 Kapiteldateien (inkl. 5 Teil-Synthesen) + Build-Skripte |
-| `bilder_04/` | **Quelle**: Diagramme (SVG/PNG) + `erzeuge_*.py`-Generatoren |
+| `bilder_04/` | **Quelle**: 33 Diagramme (SVG) + 16 `erzeuge_*.py`-Generatoren |
+| `Kritik_und_Verbesserungsvorschlaege/` | **Quelle**: die Rezensionen und Vorschläge, die den Umbau ausgelöst haben |
+| `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, 760 Seiten |
| `OR_HTML_04/` | generiert: **Mehrseiten-Website** — dieser Ordner wird veröffentlicht |
-| `Operations_Research_mit_Python_Version_04_Programme/` | generiert: 76 lauffähige Beispielprogramme |
+| `Operations_Research_mit_Python_Version_04_Programme/` | generiert: 76 Beispielprogramme |
| `Notebooks_04/` | generiert: ein Jupyter-Notebook je Kapitel |
-| `PLAN.md` / `PROGRESS.md` | das Ziel des Umbaus und der erreichte Stand |
-| `pyproject.toml` | Abhängigkeiten in Gruppen (`finance`, `large-scale`, `api`, …) |
-| `Dockerfile`, `.dockerignore` | zweistufiges Kurs-Image (führt die Programme aus, baut nicht das Buch) |
-| `pandoc-defaults-*.yaml`, `pandoc/` | Konfiguration des PDF-Baus |
**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.
+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.
---
-## Bauen
+## Das Buch selbst bauen
+
+Alle Befehle werden **in diesem Verzeichnis** ausgeführt; die Skripte leiten ihre Pfade aus
+der eigenen Position ab.
```bash
-cd Version_04
-
-# nur prüfen: Struktur, Querverweise, Codezäune, harte Nummern
+# 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
@@ -45,115 +143,89 @@ python3 Operations_Research_mit_Python_Version_04/build_version_04.py --pdf --ht
# Beispielprogramme aus den Kapiteln extrahieren
python3 Operations_Research_mit_Python_Version_04/extract_programme_04.py
+
+# ein Diagramm neu erzeugen (braucht die Gruppe [figures], teils 'dot')
+python3 bilder_04/erzeuge_kondition.py
```
-Die Website ist danach in `OR_HTML_04/` vollständig und **selbstgenügsam** — alle Verweise
-zeigen auf Unterverzeichnisse (`assets/`, `bilder_04/`, `katex/`, `Notebooks_04/`). Der Ordner
-lässt sich unverändert auf einen Webserver kopieren.
+`--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; acht Fälle wurden nachträglich gefunden, und jeder hat eine eigene
+Prüfung hinterlassen.
----
+### Externe Werkzeuge
-## Voraussetzungen
-
-**Python-Pakete** — alles auf einen Schlag, wie im Buch abgedruckt
-([`requirements.txt`](Operations_Research_mit_Python_Version_04_Programme/requirements.txt)):
-
-```bash
-python3 -m venv .venv && source .venv/bin/activate
-pip install -r Operations_Research_mit_Python_Version_04_Programme/requirements.txt
-```
-
-Wer eine schlanke Umgebung möchte, nimmt stattdessen [`pyproject.toml`](pyproject.toml) mit
-seinen Gruppen — die Grundausstattung trägt den Großteil des Buchs:
-
-```bash
-pip install -e . # numpy, scipy, pandas, matplotlib, openpyxl, pydantic, ortools, cvxpy
-pip install -e ".[finance]" # scikit-learn, yfinance
-pip install -e ".[large-scale]" # highspy, Pyomo, linopy, polars
-pip install -e ".[api]" # fastapi, uvicorn, httpx
-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 die Grundausstattung installiert, kann in den Konflikt gar nicht geraten —
-`cvxpy` zieht `highspy` **nicht** nach, erkennt es aber, sobald `[large-scale]` es
-mitgebracht hat.
-
-`graphviz` fehlt in der `requirements.txt` — es wird allein von
-`bilder_04/erzeuge_architektur_diagramme.py` gebraucht, nicht von den Beispielprogrammen,
-und steht darum nur in `[figures]`.
-
-### Oder im Container
-
-Wer nichts installieren will, baut das Kurs-Image:
-
-```bash
-docker build -t or-mit-python .
-docker run --rm or-mit-python Installationstest.py
-docker run --rm or-mit-python Rucksack.py
-docker run --rm -it or-mit-python # Python-Eingabe
-docker run --rm -v "$PWD/ausgabe:/buch/output" or-mit-python Excel_Bruecke.py
-```
-
-Zweistufiger Bau: Die erste Stufe übersetzt die Abhängigkeiten in eine virtuelle Umgebung
-und braucht dafür einen Compiler, die zweite kopiert nur das Ergebnis — die Bauumgebung
-landet nicht im Image. Enthalten sind die Gruppen `finance`, `large-scale`, `api` und `dev`;
-`figures` fehlt bewusst, weil es zusätzlich Graphviz verlangt und nur dem Neuerzeugen der
-Diagramme dient.
-
-**Das Image führt die Programme aus, es baut das Buch nicht.** Für PDF und Website braucht
-es pandoc, xelatex und inkscape — zusammen über ein Gigabyte, ohne Nutzen für jemanden, der
-die Beispiele durchrechnen will.
-
-Zwei Punkte, die nicht offensichtlich sind: Das Image enthält `ortools` **und** `highspy`,
-obwohl sie sich nicht gemeinsam importieren lassen — der Konflikt wird zur Laufzeit durch
-getrennte Prozesse gelöst, nicht durch Weglassen. Und `libgomp1` muss im schlanken
-Basis-Image nachinstalliert werden, sonst scheitert der erste Solveraufruf mit
-`libgomp.so.1: cannot open shared object file`.
-
-**Externe Werkzeuge**, je nachdem, was gebaut werden soll:
-
-| Werkzeug | wofür | ohne es |
+| 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 15 Bildgeneratoren | nur beim Neuerzeugen dieser Diagramme nötig |
+| `dot` (Graphviz) | 5 der 16 Bildgeneratoren | nur beim Neuerzeugen dieser Diagramme nötig |
**`OR_HTML_04/katex/`** ist externes Material und wird von keinem Skript erzeugt. Fehlt es,
bleiben die Formeln auf der Website ungesetzt.
----
-
-## Zwei Werte, die beim Veröffentlichen zu setzen sind
-
-1. **`COLAB_BASIS_URL`** in `Operations_Research_mit_Python_Version_04/build_version_04.py`.
- Sie trägt Kontoname und Repository-Name; die 25 Colab-Badges auf den Kapitelseiten hängen
- daran. Der Pfadteil `Notebooks_04/…` wird relativ zur Repository-Wurzel aufgelöst und
- stimmt bereits. Ein Leerstring schaltet die Badges ab.
-2. Nichts weiter. Alle übrigen Pfade sind relativ.
+**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.
---
-## Warum die PDF-Konfiguration doppelt vorliegt
+## Notebooks und Google Colab
-`pandoc-defaults-basis.yaml` ist eine Kopie der Benutzerdatei
-`~/.config/pandoc/defaults.yaml` (Schriften, Geometrie, Seitenlayout) — 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.
+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.
-`pandoc-defaults-buch.yaml` enthält die projektspezifischen Überschreibungen
-(Inhaltsverzeichnis und Nummerierung werden im Markdown selbst erzeugt, `-shell-escape` für
-`\includesvg`). Die eigentliche Buchtypografie steht in `bilder_04/or_pdf_header.tex`.
+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; alle sechs Phasen sind
-abgeschlossen. **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 der Funde unterwegs und
-der Regeln, die sich daraus ergeben haben. Wer hier weiterarbeitet, liest beide zuerst.
+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*.
diff --git a/pyproject.toml b/pyproject.toml
index 89f36f3..6f4bbcc 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -91,6 +91,12 @@ dev = [
"pytest>=8.0",
]
+# Die 25 Kapitel-Notebooks im Browser ausfuehren - ohne dass jemand etwas
+# installieren muss. Das Kurs-Image (Dockerfile) bringt diese Gruppe mit.
+notebook = [
+ "jupyterlab>=4.0",
+]
+
# pymoo wird im Buch als Bibliothek fuer genetische Algorithmen empfohlen
# (Kapitel Metaheuristiken), aber von keinem Beispielprogramm importiert.
# Deshalb steht es hier eigenstaendig und nicht in der Grundausstattung.
@@ -99,7 +105,7 @@ empfehlungen = [
]
alles = [
- "or-mit-python[finance,large-scale,api,figures,dev,empfehlungen]",
+ "or-mit-python[finance,large-scale,api,figures,dev,notebook,empfehlungen]",
]
[tool.setuptools]