diff --git a/.gitignore b/.gitignore
index e4f9760..6da6577 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,9 @@ __pycache__/
venv/
.venv/
+# pip install -e . legt dieses Verzeichnis an (Metadaten des editierbaren Pakets)
+or_mit_python.egg-info/
+
# LaTeX/xelatex-Nebenprodukte des PDF-Baus (build_version_04.py --pdf)
build_v04.log
build_v04.tex
diff --git a/CLAUDE.md b/CLAUDE.md
index c7975c1..cda9bf3 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -174,6 +174,31 @@ Auch das `README.md` im Programme-Verzeichnis wird erzeugt — aus der Vorlage
**`or_kern.py` ist der gemeinsame Unterbau** aller Programme (Domänenmodell, `SolverStatus`,
`Loesung`-DTO, Abnahmeprüfung). Abgedruckt im Kapitel Praxisfallen.
+**Notebook-Tests (`nbmake`).** Seit September 2026 sind die 25 Notebooks ebenso
+getestet wie die Programme. `pyproject.toml` hat `nbmake>=1.5` in der `dev`-Gruppe;
+`PLAN.md` Abschnitt 10 enthält den zusätzlichen Verifikationsschritt
+`pytest --nbmake --nbmake-timeout=900 Notebooks_04/ -q`. Dafür müssen die Notebooks im
+Build drei Dinge mitbringen, die ein Programmskript nicht braucht:
+* **Zweite Setup-Zelle** (`NOTEBOOK_SETUP_2` in `build_version_04.py`): definiert
+ `__file__` (Programme nutzen es für `OUTPUT_DIR`), erweitert `sys.path` um das
+ Programmverzeichnis (`from or_kern import …`) und stellt multiprocessing auf `fork`
+ (der Spawn-Modus findet Kernel-Funktionen nicht).
+* **Subprocess-Zellen** für Programme, die nicht direkt im Kernel laufen:
+ `import highspy` (Solver-Konflikt mit ortools), `import pytest` (`SystemExit`),
+ `get_context("spawn")` (multiprocessing), `import subprocess` + `__file__` (sucht
+ andere .py-Dateien), `sys.exit(` (`SystemExit` im Kernel). Diese Programme erscheinen
+ als Markdown-Codeblock (sichtbar, nicht ausführbar) plus einer Subprocess-Zelle, die
+ das Programm mit `NO_COLOR=1` aufruft (sonst gibt pytest ANSI-Codes aus, die der
+ Ausgabe-Parser von `Mutationstest.py` nicht verarbeitet).
+* **`%pip`-Setup-Zelle** trägt das Tag `nbmake-skip` (wird von nbmake aktuell noch
+ ignoriert, aber als Dokumentation gedacht — die Zelle ist schnell, wenn Pakete schon
+ installiert sind).
+* **Inkrementeller Test** (`teste_code_04.py`): testet nur Programme und Notebooks,
+ die sich seit dem letzten Commit geändert haben (via `git status`). Netzabhängige
+ Dateien (yfinance) werden automatisch erkannt und nur getestet, wenn Internet
+ verfügbar ist. Aufruf: `python3 …/teste_code_04.py` (geänderte), `--alles`
+ (alle), `--check` (nur anzeigen).
+
**`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:
diff --git a/Notebooks_04/anhang-fehlerdiagnose.ipynb b/Notebooks_04/anhang-fehlerdiagnose.ipynb
index 9a10276..e69d310 100644
--- a/Notebooks_04/anhang-fehlerdiagnose.ipynb
+++ b/Notebooks_04/anhang-fehlerdiagnose.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Wenn die fünf Schritte nicht reichen: den Konflikt einkreisen\n",
+ "\n",
+ "`Konfliktsuche.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/Notebooks_04/bruecke.ipynb b/Notebooks_04/bruecke.ipynb
index 08a2fd8..b54ab56 100644
--- a/Notebooks_04/bruecke.ipynb
+++ b/Notebooks_04/bruecke.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Programm\n",
+ "\n",
+ "`Strukturbruecke.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/Notebooks_04/cpsat.ipynb b/Notebooks_04/cpsat.ipynb
index 765a79b..dd73a5d 100644
--- a/Notebooks_04/cpsat.ipynb
+++ b/Notebooks_04/cpsat.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Was Propagation leistet\n",
+ "\n",
+ "`Propagation_Demo.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -116,6 +150,15 @@
"`CP_SAT_Vertretungssystem.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxisfall: Dynamisches Vertretungssystem\n",
+ "\n",
+ "`CP_SAT_Vertretungssystem.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -375,6 +418,15 @@
"`JobShop_Intervalle.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Intervallvariablen: Job-Shop-Scheduling\n",
+ "\n",
+ "`JobShop_Intervalle.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -505,6 +557,15 @@
"`Parallele_Suche.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Parallele Suche: was `num_workers` wirklich bewirkt\n",
+ "\n",
+ "`Parallele_Suche.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -717,6 +778,15 @@
"`CP_SAT_Statusfaelle.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die fünf Antworten von CP-SAT\n",
+ "\n",
+ "`CP_SAT_Statusfaelle.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -957,6 +1027,15 @@
"`Strafgewichte.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Strafgewichte.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/cvar.ipynb b/Notebooks_04/cvar.ipynb
index f581980..c4e7564 100644
--- a/Notebooks_04/cvar.ipynb
+++ b/Notebooks_04/cvar.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die zwei Schwächen des Markowitz-Modells\n",
+ "\n",
+ "`VaR_CVaR_Demo.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -152,6 +186,15 @@
"`CVaR_Portfolio.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Implementierung: CVaR-Portfolio mit Reibung\n",
+ "\n",
+ "`CVaR_Portfolio.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/dekomposition.ipynb b/Notebooks_04/dekomposition.ipynb
index 321bd40..8ac622c 100644
--- a/Notebooks_04/dekomposition.ipynb
+++ b/Notebooks_04/dekomposition.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Programm\n",
+ "\n",
+ "`Spaltengenerierung.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/Notebooks_04/dynamische-programmierung.ipynb b/Notebooks_04/dynamische-programmierung.ipynb
index 2662789..e3c4be0 100644
--- a/Notebooks_04/dynamische-programmierung.ipynb
+++ b/Notebooks_04/dynamische-programmierung.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die Bellman-Gleichung\n",
+ "\n",
+ "`Bellman_Minimalbeispiel.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -132,6 +166,15 @@
"`Mehrperiodige_Order_Execution.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Almgren-Chriss-Problem: optimale Orderausführung\n",
+ "\n",
+ "`Mehrperiodige_Order_Execution.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/einfuehrung.ipynb b/Notebooks_04/einfuehrung.ipynb
index 0531984..778fa0a 100644
--- a/Notebooks_04/einfuehrung.ipynb
+++ b/Notebooks_04/einfuehrung.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Warum Ausprobieren scheitert — mit eigener Rechnung\n",
+ "\n",
+ "`Brute_Force_Vergleich.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -150,6 +184,15 @@
"`Bausteine_Vorlage.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die Bausteine in der Praxis: eine Vorlage\n",
+ "\n",
+ "`Bausteine_Vorlage.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -250,6 +293,15 @@
"`Bot_Allokation.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Umsetzung mit Google OR-Tools\n",
+ "\n",
+ "`Bot_Allokation.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -363,6 +415,15 @@
"`Excel_Bruecke.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Von Excel zu Python: Ihre Daten liegen schon da\n",
+ "\n",
+ "`Excel_Bruecke.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/finanzdaten.ipynb b/Notebooks_04/finanzdaten.ipynb
index 324b2cf..86d86cd 100644
--- a/Notebooks_04/finanzdaten.ipynb
+++ b/Notebooks_04/finanzdaten.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Logarithmische Rendite{idx:Logarithmische Rendite}\n",
+ "\n",
+ "`Renditen_Vergleich.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -115,6 +149,15 @@
"`Schaetzrauschen_Demo.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Schätzfehler{idx:Schätzfehler}-Problem\n",
+ "\n",
+ "`Schaetzrauschen_Demo.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -228,6 +271,15 @@
"`Finanzdaten_Ledoit_Wolf.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxis: Datenpipeline mit korrekter Spaltenreihenfolge\n",
+ "\n",
+ "`Finanzdaten_Ledoit_Wolf.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -392,6 +444,15 @@
"`Kovarianz_Falle.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Kovarianz_Falle.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/fundament.ipynb b/Notebooks_04/fundament.ipynb
index 37b2294..71ddefe 100644
--- a/Notebooks_04/fundament.ipynb
+++ b/Notebooks_04/fundament.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Dasselbe in Python\n",
+ "\n",
+ "`Matrixform.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -101,6 +135,15 @@
"`Konvexitaet_Demo.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Konvexität sichtbar machen\n",
+ "\n",
+ "`Konvexitaet_Demo.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -191,6 +234,15 @@
"`Visualisierung_Loesungsraum.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Geometrische Visualisierung des Lösungsraums\n",
+ "\n",
+ "`Visualisierung_Loesungsraum.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -348,6 +400,15 @@
"`Skalierung_Kondition.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Experiment\n",
+ "\n",
+ "`Skalierung_Kondition.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/graphen.ipynb b/Notebooks_04/graphen.ipynb
index a9f3047..8c5b1cc 100644
--- a/Notebooks_04/graphen.ipynb
+++ b/Notebooks_04/graphen.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Minimum-Cost-Flow-Problem (MCNFP)\n",
+ "\n",
+ "`Min_Cost_Flow.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -144,6 +178,15 @@
"`Zuordnung_Ungarisch.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Der Satz von Birkhoff und von Neumann{idx:Satz von Birkhoff und von Neumann}\n",
+ "\n",
+ "`Zuordnung_Ungarisch.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -266,6 +309,15 @@
"`VRP_Flotten_Routing.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxisbeispiel: Flotten-Routing\n",
+ "\n",
+ "`VRP_Flotten_Routing.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -448,6 +500,15 @@
"`VRP_Kapazitaetsfalle.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`VRP_Kapazitaetsfalle.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/handelsmaschine.ipynb b/Notebooks_04/handelsmaschine.ipynb
index 3d7b425..0a3441e 100644
--- a/Notebooks_04/handelsmaschine.ipynb
+++ b/Notebooks_04/handelsmaschine.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die Engine\n",
+ "\n",
+ "`QuantitativeTradingEngine.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -352,6 +386,15 @@
"`Backtest_Fallen.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die fünf Selbsttäuschungen des Backtestens\n",
+ "\n",
+ "`Backtest_Fallen.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -465,6 +508,15 @@
"`Data_Snooping.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Data_Snooping.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/lp.ipynb b/Notebooks_04/lp.ipynb
index de95953..a30cdc4 100644
--- a/Notebooks_04/lp.ipynb
+++ b/Notebooks_04/lp.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Simplex-Tableau in Python\n",
+ "\n",
+ "`Simplex_Tableau_LP.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -207,6 +241,15 @@
"`Dualitaet_Nachweis.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Der starke Dualitätssatz\n",
+ "\n",
+ "`Dualitaet_Nachweis.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -302,6 +345,15 @@
"`Sensitivitaetsanalyse.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxisfall: Sensitivitätsanalyse mit korrekten Schattenpreisen\n",
+ "\n",
+ "`Sensitivitaetsanalyse.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -429,6 +481,15 @@
"`Toleranzen_und_Entartung.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die ehrliche Auskunft: Schattenpreis-Spannen\n",
+ "\n",
+ "`Toleranzen_und_Entartung.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/markowitz.ipynb b/Notebooks_04/markowitz.ipynb
index d8f490a..c793463 100644
--- a/Notebooks_04/markowitz.ipynb
+++ b/Notebooks_04/markowitz.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Warum Diversifikation funktioniert\n",
+ "\n",
+ "`Diversifikation_Demo.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -94,6 +128,15 @@
"`Markowitz_CVXPY.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Vollimplementierung mit CVXPY\n",
+ "\n",
+ "`Markowitz_CVXPY.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -374,6 +417,15 @@
"`Renditeschaetzung_Falle.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Renditeschaetzung_Falle.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/mehrziel.ipynb b/Notebooks_04/mehrziel.ipynb
index e1cb7ca..cc98354 100644
--- a/Notebooks_04/mehrziel.ipynb
+++ b/Notebooks_04/mehrziel.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Programm\n",
+ "\n",
+ "`Mehrziel_Pareto.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/Notebooks_04/metaheuristiken.ipynb b/Notebooks_04/metaheuristiken.ipynb
index 9be40f6..99b8048 100644
--- a/Notebooks_04/metaheuristiken.ipynb
+++ b/Notebooks_04/metaheuristiken.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Simulated Annealing\n",
+ "\n",
+ "`Simulated_Annealing.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -346,6 +380,15 @@
"`Metaheuristik_vs_Exakt.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Ab wann lohnt es sich?\n",
+ "\n",
+ "`Metaheuristik_vs_Exakt.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -595,6 +638,15 @@
"`Large_Neighborhood_Search.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Large Neighborhood Search\n",
+ "\n",
+ "`Large_Neighborhood_Search.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/milp.ipynb b/Notebooks_04/milp.ipynb
index 8f5ed61..ffbf18d 100644
--- a/Notebooks_04/milp.ipynb
+++ b/Notebooks_04/milp.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Warum Runden fundamental scheitert\n",
+ "\n",
+ "`Runden_Gegenbeispiel.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -138,6 +172,15 @@
"`Rucksack.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Beispiel: Das Rucksackproblem{idx:Rucksackproblem}\n",
+ "\n",
+ "`Rucksack.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -213,6 +256,15 @@
"`MILP_Portfolio_Fixgebuehren.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxisfall: Portfolio mit Ordergebühren und Kardinalitätsgrenze\n",
+ "\n",
+ "`MILP_Portfolio_Fixgebuehren.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -434,11 +486,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Alle Statusfälle behandeln\n",
+ "\n",
+ "`Solverstatus_und_Gap.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Solverstatus_und_Gap.py\n",
@@ -654,7 +709,36 @@
" print(\" Warmstart_Effekt.py - dort halbiert ein Heuristik-Hinweis die Zeit).\")\n",
" print(\" * Sie planen laufend neu und der gestrige Plan ist fast noch gueltig.\")\n",
" print(\"In beiden Faellen gilt: MESSEN, nicht glauben.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Solverstatus_und_Gap.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Solverstatus_und_Gap.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Solverstatus_und_Gap.py nicht gefunden')"
]
},
{
@@ -667,11 +751,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Warm-Starts: was sie können und was nicht\n",
+ "\n",
+ "`Warmstart_Effekt.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Warmstart_Effekt.py\n",
@@ -819,7 +906,36 @@
" print(\" davon ab, wie schnell der Solver von allein eine vergleichbar gute\")\n",
" print(\" Loesung findet. Das ist die eigentliche Lehre: Ein Warm-Start ist\")\n",
" print(\" eine Messung wert, keine Glaubensfrage.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Warmstart_Effekt.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Warmstart_Effekt.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Warmstart_Effekt.py nicht gefunden')"
]
},
{
@@ -832,11 +948,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Big_M_Falle.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Big_M_Falle.py\n",
@@ -1030,7 +1149,36 @@
" print(\"nicht darauf: Presolve kann das nur, wenn eine implizite Schranke\")\n",
" print(\"herleitbar ist. Die Pruefung aus pruefe() kostet Millisekunden und\")\n",
" print(\"funktioniert immer.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Big_M_Falle.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Big_M_Falle.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Big_M_Falle.py nicht gefunden')"
]
}
],
diff --git a/Notebooks_04/modellierung.ipynb b/Notebooks_04/modellierung.ipynb
index df7ab93..c44b88d 100644
--- a/Notebooks_04/modellierung.ipynb
+++ b/Notebooks_04/modellierung.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Derselbe Datensatz, fünf Modelle\n",
+ "\n",
+ "`Vom_Wunsch_zum_Modell.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/Notebooks_04/oekosystem.ipynb b/Notebooks_04/oekosystem.ipynb
index ae42071..6518796 100644
--- a/Notebooks_04/oekosystem.ipynb
+++ b/Notebooks_04/oekosystem.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die Entscheidung in drei Fragen\n",
+ "\n",
+ "`Solver_Wahl.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -121,11 +155,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Wie die Isolation aussieht, wenn sie tragen soll\n",
+ "\n",
+ "`Ein_System_Vier_Ansaetze.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Ein_System_Vier_Ansaetze.py\n",
@@ -270,7 +307,45 @@
" print(\"Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.\")\n",
" print(\"(Die Zeiten enthalten Prozessstart und Import - sie messen NICHT die\")\n",
" print(\" reine Solverleistung. Die Uebungsaufgabe 'Laufzeitvergleich' trennt beides.)\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Ein_System_Vier_Ansaetze.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Ein_System_Vier_Ansaetze.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Ein_System_Vier_Ansaetze.py nicht gefunden')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die Schichten im Überblick\n",
+ "\n",
+ "`Modellierungsschichten.py`\n"
]
},
{
@@ -470,11 +545,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Vier Stufen an einem Transportproblem\n",
+ "\n",
+ "`Vektorisierte_Modellgenerierung.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Vektorisierte_Modellgenerierung.py\n",
@@ -714,7 +792,36 @@
" print(\" Schreibweise derselben Matrix - Schleife gegen Kronecker-Produkt.\")\n",
" print(\"\\nDie Lesbarkeit von Variante A ist trotzdem viel wert: Fangen Sie dort an,\")\n",
" print(\"und vektorisieren Sie erst, wenn die Messung es verlangt.\")\n",
- " print(\"=\" * 88)"
+ " print(\"=\" * 88)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Vektorisierte_Modellgenerierung.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Vektorisierte_Modellgenerierung.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Vektorisierte_Modellgenerierung.py nicht gefunden')"
]
}
],
diff --git a/Notebooks_04/output/feasible_region_2d.png b/Notebooks_04/output/feasible_region_2d.png
new file mode 100644
index 0000000..ff86ad3
Binary files /dev/null and b/Notebooks_04/output/feasible_region_2d.png differ
diff --git a/Notebooks_04/output/markowitz_efficient_frontier.png b/Notebooks_04/output/markowitz_efficient_frontier.png
new file mode 100644
index 0000000..15e2d8d
Binary files /dev/null and b/Notebooks_04/output/markowitz_efficient_frontier.png differ
diff --git a/Notebooks_04/output/optimal_execution_dp.png b/Notebooks_04/output/optimal_execution_dp.png
new file mode 100644
index 0000000..8c44182
Binary files /dev/null and b/Notebooks_04/output/optimal_execution_dp.png differ
diff --git a/Notebooks_04/output/trading_engine_backtest.png b/Notebooks_04/output/trading_engine_backtest.png
new file mode 100644
index 0000000..e5d84d4
Binary files /dev/null and b/Notebooks_04/output/trading_engine_backtest.png differ
diff --git a/Notebooks_04/praxisfallen.ipynb b/Notebooks_04/praxisfallen.ipynb
index 802e3be..9489dd9 100644
--- a/Notebooks_04/praxisfallen.ipynb
+++ b/Notebooks_04/praxisfallen.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,27 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -31,11 +56,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Falle 1 — Infeasibility{idx:Infeasibility}\n",
+ "\n",
+ "`Infeasibility_Diagnose.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Infeasibility_Diagnose.py\n",
@@ -147,7 +175,36 @@
" print(\"\\nDer Anwender bekommt jetzt einen Plan PLUS eine konkrete Ursache -\")\n",
" print(\"und kann handeln: Vertretung von aussen holen, Stunde verlegen,\")\n",
" print(\"oder die Klasse zusammenlegen.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Infeasibility_Diagnose.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Infeasibility_Diagnose.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Infeasibility_Diagnose.py nicht gefunden')"
]
},
{
@@ -160,11 +217,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Falle 2 — Der Black-Box-Effekt\n",
+ "\n",
+ "`Erklaerbarkeit.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Erklaerbarkeit.py\n",
@@ -249,7 +309,45 @@
" print(f\" -> Die Obergrenze blockiert zwar {blockiert}, kostet aber gerade NICHTS:\")\n",
" print(\" Selbst ohne sie waere die Loesung dieselbe.\")\n",
" print(\" Blockierend und kostenrelevant sind zwei verschiedene Dinge.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Erklaerbarkeit.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Erklaerbarkeit.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Erklaerbarkeit.py nicht gefunden')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Constraint Attribution{idx:Constraint Attribution}: welche Bedingung kostet wie viel?\n",
+ "\n",
+ "`Constraint_Attribution.py`\n"
]
},
{
@@ -584,11 +682,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Ein Enum für fünf Bibliotheken\n",
+ "\n",
+ "`or_kern.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# or_kern.py\n",
@@ -1092,7 +1193,36 @@
" print(\"\\nAlle drei scheitern beim EINLESEN - nicht erst beim Loesen und\")\n",
" print(\"schon gar nicht erst im Bericht. Das ist der ganze Zweck der\")\n",
" print(\"Domaenenschicht.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'or_kern.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'or_kern.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('or_kern.py nicht gefunden')"
]
},
{
@@ -1105,11 +1235,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Der Solverwechsel in der Praxis\n",
+ "\n",
+ "`Solverwechsel_CPSAT_HiGHS.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Solverwechsel_CPSAT_HiGHS.py\n",
@@ -1433,7 +1566,36 @@
" print(\"gleich teure Loesungen, darf jeder Solver eine andere davon liefern.\")\n",
" print(\"Hier stimmen sie zufaellig ueberein - darauf zu testen waere trotzdem\")\n",
" print(\"ein unzuverlaessiger Test (siehe JobShop_Intervalle.py).\")\n",
- " print(\"=\" * 82)"
+ " print(\"=\" * 82)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Solverwechsel_CPSAT_HiGHS.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Solverwechsel_CPSAT_HiGHS.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Solverwechsel_CPSAT_HiGHS.py nicht gefunden')"
]
},
{
@@ -1446,11 +1608,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Betriebsueberwachung.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Betriebsueberwachung.py\n",
@@ -1630,7 +1795,36 @@
" print()\n",
" print(\"Die dritte ist die frueheste Warnung: Sie steigt, lange bevor der Gap\")\n",
" print(\"sichtbar wird, und gibt Zeit zum Handeln.\")\n",
- " print(\"=\" * 84)"
+ " print(\"=\" * 84)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Betriebsueberwachung.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Betriebsueberwachung.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Betriebsueberwachung.py nicht gefunden')"
]
}
],
diff --git a/Notebooks_04/prognose.ipynb b/Notebooks_04/prognose.ipynb
index cf10cf6..3f44049 100644
--- a/Notebooks_04/prognose.ipynb
+++ b/Notebooks_04/prognose.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Programm\n",
+ "\n",
+ "`Predict_then_Optimize.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/Notebooks_04/qp-nlp.ipynb b/Notebooks_04/qp-nlp.ipynb
index 92f9c40..21a6fb3 100644
--- a/Notebooks_04/qp-nlp.ipynb
+++ b/Notebooks_04/qp-nlp.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Konvexität: die präzise Aussage\n",
+ "\n",
+ "`QP_Grundlagen.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -102,6 +136,15 @@
"`KKT_Nachweis.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die vier KKT-Bedingungen\n",
+ "\n",
+ "`KKT_Nachweis.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -229,6 +272,15 @@
"`Entropie_Maximierte_Allokation.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxisfall: Entropie-maximierte Kapitalallokation\n",
+ "\n",
+ "`Entropie_Maximierte_Allokation.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -384,6 +436,15 @@
"`Lokale_Optima_Multistart.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Was ein lokales Optimum praktisch bedeutet\n",
+ "\n",
+ "`Lokale_Optima_Multistart.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/supplychain.ipynb b/Notebooks_04/supplychain.ipynb
index b5842f0..e3419cc 100644
--- a/Notebooks_04/supplychain.ipynb
+++ b/Notebooks_04/supplychain.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Programm\n",
+ "\n",
+ "`Kraftwerkseinsatz.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/Notebooks_04/testing.ipynb b/Notebooks_04/testing.ipynb
index 7ce275c..37cefa1 100644
--- a/Notebooks_04/testing.ipynb
+++ b/Notebooks_04/testing.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,27 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -31,11 +56,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Die Testsuite\n",
+ "\n",
+ "`test_or_kern.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# test_or_kern.py\n",
@@ -344,7 +372,36 @@
"\n",
"if __name__ == \"__main__\":\n",
" import sys\n",
- " sys.exit(pytest.main([__file__, \"-v\", \"--tb=short\", \"-p\", \"no:cacheprovider\"]))"
+ " sys.exit(pytest.main([__file__, \"-v\", \"--tb=short\", \"-p\", \"no:cacheprovider\"]))\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'test_or_kern.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'test_or_kern.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('test_or_kern.py nicht gefunden')"
]
},
{
@@ -357,11 +414,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Wer testet die Tests?\n",
+ "\n",
+ "`Mutationstest.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Mutationstest.py\n",
@@ -540,7 +600,36 @@
" print(\"Die Quote selbst ist keine Kennzahl fuer ein Dashboard. Zehn von Hand\")\n",
" print(\"gewaehlte Mutationen sind keine Stichprobe aus der Menge aller\")\n",
" print(\"moeglichen Fehler. Was zaehlt, ist die LISTE der Ueberlebenden.\")\n",
- " print(\"=\" * 84)"
+ " print(\"=\" * 84)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Mutationstest.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Mutationstest.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Mutationstest.py nicht gefunden')"
]
},
{
@@ -553,11 +642,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Ein Vergleich, dem man glauben kann\n",
+ "\n",
+ "`Benchmark_Skalierung.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Benchmark_Skalierung.py\n",
@@ -770,7 +862,36 @@
" print(\" * Etwas ueber Ihre Maschine. Diese Zahlen stammen von einer\")\n",
" print(\" anderen. Der Sinn des Programms ist, dass Sie es auf Ihrer\")\n",
" print(\" laufen lassen.\")\n",
- " print(\"=\" * 92)"
+ " print(\"=\" * 92)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Benchmark_Skalierung.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Benchmark_Skalierung.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Benchmark_Skalierung.py nicht gefunden')"
]
},
{
@@ -783,11 +904,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Das Modell als Dienst\n",
+ "\n",
+ "`Optimierungsdienst.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Optimierungsdienst.py\n",
@@ -1102,7 +1226,36 @@
" print()\n",
" print(\"(Es wird hier nicht gebaut - das Buch setzt keine laufende\")\n",
" print(\" Docker-Installation voraus.)\")\n",
- " print(\"=\" * 80)"
+ " print(\"=\" * 80)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Optimierungsdienst.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Optimierungsdienst.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Optimierungsdienst.py nicht gefunden')"
]
}
],
diff --git a/Notebooks_04/unsicherheit.ipynb b/Notebooks_04/unsicherheit.ipynb
index 2e2a12b..0b4ca21 100644
--- a/Notebooks_04/unsicherheit.ipynb
+++ b/Notebooks_04/unsicherheit.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Der Fluch des Durchschnitts\n",
+ "\n",
+ "`Fluch_des_Durchschnitts.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -100,6 +134,15 @@
"`Monte_Carlo.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Monte-Carlo-Simulation{idx:Monte-Carlo-Simulation}\n",
+ "\n",
+ "`Monte_Carlo.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -209,6 +252,15 @@
"`Stochastische_Optimierung.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Zweistufige stochastische Programmierung\n",
+ "\n",
+ "`Stochastische_Optimierung.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -345,6 +397,15 @@
"`Robuste_Optimierung.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Robuste Optimierung{idx:Robuste Optimierung}: gegen den Worst Case absichern\n",
+ "\n",
+ "`Robuste_Optimierung.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -461,6 +522,15 @@
"`Chance_Constraints.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Der Fall: ein Kraftwerkspark mit 500 MW Zusage\n",
+ "\n",
+ "`Chance_Constraints.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/Notebooks_04/vorwort.ipynb b/Notebooks_04/vorwort.ipynb
index 66c47aa..b9a4ced 100644
--- a/Notebooks_04/vorwort.ipynb
+++ b/Notebooks_04/vorwort.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,27 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -31,11 +56,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Installationstest {-}\n",
+ "\n",
+ "`Installationstest.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Installationstest.py\n",
@@ -154,7 +182,36 @@
" print(\"-\" * 78)\n",
" print(\"Alles bereit — Sie können mit dem ersten Kapitel beginnen.\"\n",
" if alle_ok else \"Mindestens ein Solver arbeitet fehlerhaft.\")\n",
- " sys.exit(0 if alle_ok else 1)"
+ " sys.exit(0 if alle_ok else 1)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Installationstest.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Installationstest.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Installationstest.py nicht gefunden')"
]
}
],
diff --git a/OR_HTML_04/Notebooks_04.zip b/OR_HTML_04/Notebooks_04.zip
index 67ded28..e9fdbef 100644
Binary files a/OR_HTML_04/Notebooks_04.zip and b/OR_HTML_04/Notebooks_04.zip differ
diff --git a/OR_HTML_04/Notebooks_04/anhang-fehlerdiagnose.ipynb b/OR_HTML_04/Notebooks_04/anhang-fehlerdiagnose.ipynb
index 9a10276..e69d310 100644
--- a/OR_HTML_04/Notebooks_04/anhang-fehlerdiagnose.ipynb
+++ b/OR_HTML_04/Notebooks_04/anhang-fehlerdiagnose.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Wenn die fünf Schritte nicht reichen: den Konflikt einkreisen\n",
+ "\n",
+ "`Konfliktsuche.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/OR_HTML_04/Notebooks_04/bruecke.ipynb b/OR_HTML_04/Notebooks_04/bruecke.ipynb
index 08a2fd8..b54ab56 100644
--- a/OR_HTML_04/Notebooks_04/bruecke.ipynb
+++ b/OR_HTML_04/Notebooks_04/bruecke.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Programm\n",
+ "\n",
+ "`Strukturbruecke.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/OR_HTML_04/Notebooks_04/cpsat.ipynb b/OR_HTML_04/Notebooks_04/cpsat.ipynb
index 765a79b..dd73a5d 100644
--- a/OR_HTML_04/Notebooks_04/cpsat.ipynb
+++ b/OR_HTML_04/Notebooks_04/cpsat.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Was Propagation leistet\n",
+ "\n",
+ "`Propagation_Demo.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -116,6 +150,15 @@
"`CP_SAT_Vertretungssystem.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxisfall: Dynamisches Vertretungssystem\n",
+ "\n",
+ "`CP_SAT_Vertretungssystem.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -375,6 +418,15 @@
"`JobShop_Intervalle.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Intervallvariablen: Job-Shop-Scheduling\n",
+ "\n",
+ "`JobShop_Intervalle.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -505,6 +557,15 @@
"`Parallele_Suche.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Parallele Suche: was `num_workers` wirklich bewirkt\n",
+ "\n",
+ "`Parallele_Suche.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -717,6 +778,15 @@
"`CP_SAT_Statusfaelle.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die fünf Antworten von CP-SAT\n",
+ "\n",
+ "`CP_SAT_Statusfaelle.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -957,6 +1027,15 @@
"`Strafgewichte.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Strafgewichte.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/cvar.ipynb b/OR_HTML_04/Notebooks_04/cvar.ipynb
index f581980..c4e7564 100644
--- a/OR_HTML_04/Notebooks_04/cvar.ipynb
+++ b/OR_HTML_04/Notebooks_04/cvar.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die zwei Schwächen des Markowitz-Modells\n",
+ "\n",
+ "`VaR_CVaR_Demo.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -152,6 +186,15 @@
"`CVaR_Portfolio.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Implementierung: CVaR-Portfolio mit Reibung\n",
+ "\n",
+ "`CVaR_Portfolio.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/dekomposition.ipynb b/OR_HTML_04/Notebooks_04/dekomposition.ipynb
index 321bd40..8ac622c 100644
--- a/OR_HTML_04/Notebooks_04/dekomposition.ipynb
+++ b/OR_HTML_04/Notebooks_04/dekomposition.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Programm\n",
+ "\n",
+ "`Spaltengenerierung.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/OR_HTML_04/Notebooks_04/dynamische-programmierung.ipynb b/OR_HTML_04/Notebooks_04/dynamische-programmierung.ipynb
index 2662789..e3c4be0 100644
--- a/OR_HTML_04/Notebooks_04/dynamische-programmierung.ipynb
+++ b/OR_HTML_04/Notebooks_04/dynamische-programmierung.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die Bellman-Gleichung\n",
+ "\n",
+ "`Bellman_Minimalbeispiel.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -132,6 +166,15 @@
"`Mehrperiodige_Order_Execution.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Almgren-Chriss-Problem: optimale Orderausführung\n",
+ "\n",
+ "`Mehrperiodige_Order_Execution.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/einfuehrung.ipynb b/OR_HTML_04/Notebooks_04/einfuehrung.ipynb
index 0531984..778fa0a 100644
--- a/OR_HTML_04/Notebooks_04/einfuehrung.ipynb
+++ b/OR_HTML_04/Notebooks_04/einfuehrung.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Warum Ausprobieren scheitert — mit eigener Rechnung\n",
+ "\n",
+ "`Brute_Force_Vergleich.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -150,6 +184,15 @@
"`Bausteine_Vorlage.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die Bausteine in der Praxis: eine Vorlage\n",
+ "\n",
+ "`Bausteine_Vorlage.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -250,6 +293,15 @@
"`Bot_Allokation.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Umsetzung mit Google OR-Tools\n",
+ "\n",
+ "`Bot_Allokation.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -363,6 +415,15 @@
"`Excel_Bruecke.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Von Excel zu Python: Ihre Daten liegen schon da\n",
+ "\n",
+ "`Excel_Bruecke.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/finanzdaten.ipynb b/OR_HTML_04/Notebooks_04/finanzdaten.ipynb
index 324b2cf..86d86cd 100644
--- a/OR_HTML_04/Notebooks_04/finanzdaten.ipynb
+++ b/OR_HTML_04/Notebooks_04/finanzdaten.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Logarithmische Rendite{idx:Logarithmische Rendite}\n",
+ "\n",
+ "`Renditen_Vergleich.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -115,6 +149,15 @@
"`Schaetzrauschen_Demo.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Schätzfehler{idx:Schätzfehler}-Problem\n",
+ "\n",
+ "`Schaetzrauschen_Demo.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -228,6 +271,15 @@
"`Finanzdaten_Ledoit_Wolf.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxis: Datenpipeline mit korrekter Spaltenreihenfolge\n",
+ "\n",
+ "`Finanzdaten_Ledoit_Wolf.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -392,6 +444,15 @@
"`Kovarianz_Falle.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Kovarianz_Falle.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/fundament.ipynb b/OR_HTML_04/Notebooks_04/fundament.ipynb
index 37b2294..71ddefe 100644
--- a/OR_HTML_04/Notebooks_04/fundament.ipynb
+++ b/OR_HTML_04/Notebooks_04/fundament.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Dasselbe in Python\n",
+ "\n",
+ "`Matrixform.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -101,6 +135,15 @@
"`Konvexitaet_Demo.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Konvexität sichtbar machen\n",
+ "\n",
+ "`Konvexitaet_Demo.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -191,6 +234,15 @@
"`Visualisierung_Loesungsraum.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Geometrische Visualisierung des Lösungsraums\n",
+ "\n",
+ "`Visualisierung_Loesungsraum.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -348,6 +400,15 @@
"`Skalierung_Kondition.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Experiment\n",
+ "\n",
+ "`Skalierung_Kondition.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/graphen.ipynb b/OR_HTML_04/Notebooks_04/graphen.ipynb
index a9f3047..8c5b1cc 100644
--- a/OR_HTML_04/Notebooks_04/graphen.ipynb
+++ b/OR_HTML_04/Notebooks_04/graphen.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Minimum-Cost-Flow-Problem (MCNFP)\n",
+ "\n",
+ "`Min_Cost_Flow.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -144,6 +178,15 @@
"`Zuordnung_Ungarisch.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Der Satz von Birkhoff und von Neumann{idx:Satz von Birkhoff und von Neumann}\n",
+ "\n",
+ "`Zuordnung_Ungarisch.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -266,6 +309,15 @@
"`VRP_Flotten_Routing.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxisbeispiel: Flotten-Routing\n",
+ "\n",
+ "`VRP_Flotten_Routing.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -448,6 +500,15 @@
"`VRP_Kapazitaetsfalle.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`VRP_Kapazitaetsfalle.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/handelsmaschine.ipynb b/OR_HTML_04/Notebooks_04/handelsmaschine.ipynb
index 3d7b425..0a3441e 100644
--- a/OR_HTML_04/Notebooks_04/handelsmaschine.ipynb
+++ b/OR_HTML_04/Notebooks_04/handelsmaschine.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die Engine\n",
+ "\n",
+ "`QuantitativeTradingEngine.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -352,6 +386,15 @@
"`Backtest_Fallen.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die fünf Selbsttäuschungen des Backtestens\n",
+ "\n",
+ "`Backtest_Fallen.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -465,6 +508,15 @@
"`Data_Snooping.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Data_Snooping.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/lp.ipynb b/OR_HTML_04/Notebooks_04/lp.ipynb
index de95953..a30cdc4 100644
--- a/OR_HTML_04/Notebooks_04/lp.ipynb
+++ b/OR_HTML_04/Notebooks_04/lp.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Simplex-Tableau in Python\n",
+ "\n",
+ "`Simplex_Tableau_LP.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -207,6 +241,15 @@
"`Dualitaet_Nachweis.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Der starke Dualitätssatz\n",
+ "\n",
+ "`Dualitaet_Nachweis.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -302,6 +345,15 @@
"`Sensitivitaetsanalyse.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxisfall: Sensitivitätsanalyse mit korrekten Schattenpreisen\n",
+ "\n",
+ "`Sensitivitaetsanalyse.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -429,6 +481,15 @@
"`Toleranzen_und_Entartung.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die ehrliche Auskunft: Schattenpreis-Spannen\n",
+ "\n",
+ "`Toleranzen_und_Entartung.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/markowitz.ipynb b/OR_HTML_04/Notebooks_04/markowitz.ipynb
index d8f490a..c793463 100644
--- a/OR_HTML_04/Notebooks_04/markowitz.ipynb
+++ b/OR_HTML_04/Notebooks_04/markowitz.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Warum Diversifikation funktioniert\n",
+ "\n",
+ "`Diversifikation_Demo.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -94,6 +128,15 @@
"`Markowitz_CVXPY.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Vollimplementierung mit CVXPY\n",
+ "\n",
+ "`Markowitz_CVXPY.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -374,6 +417,15 @@
"`Renditeschaetzung_Falle.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Renditeschaetzung_Falle.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/mehrziel.ipynb b/OR_HTML_04/Notebooks_04/mehrziel.ipynb
index e1cb7ca..cc98354 100644
--- a/OR_HTML_04/Notebooks_04/mehrziel.ipynb
+++ b/OR_HTML_04/Notebooks_04/mehrziel.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Programm\n",
+ "\n",
+ "`Mehrziel_Pareto.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/OR_HTML_04/Notebooks_04/metaheuristiken.ipynb b/OR_HTML_04/Notebooks_04/metaheuristiken.ipynb
index 9be40f6..99b8048 100644
--- a/OR_HTML_04/Notebooks_04/metaheuristiken.ipynb
+++ b/OR_HTML_04/Notebooks_04/metaheuristiken.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Simulated Annealing\n",
+ "\n",
+ "`Simulated_Annealing.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -346,6 +380,15 @@
"`Metaheuristik_vs_Exakt.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Ab wann lohnt es sich?\n",
+ "\n",
+ "`Metaheuristik_vs_Exakt.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -595,6 +638,15 @@
"`Large_Neighborhood_Search.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Large Neighborhood Search\n",
+ "\n",
+ "`Large_Neighborhood_Search.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/milp.ipynb b/OR_HTML_04/Notebooks_04/milp.ipynb
index 8f5ed61..ffbf18d 100644
--- a/OR_HTML_04/Notebooks_04/milp.ipynb
+++ b/OR_HTML_04/Notebooks_04/milp.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Warum Runden fundamental scheitert\n",
+ "\n",
+ "`Runden_Gegenbeispiel.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -138,6 +172,15 @@
"`Rucksack.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Beispiel: Das Rucksackproblem{idx:Rucksackproblem}\n",
+ "\n",
+ "`Rucksack.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -213,6 +256,15 @@
"`MILP_Portfolio_Fixgebuehren.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxisfall: Portfolio mit Ordergebühren und Kardinalitätsgrenze\n",
+ "\n",
+ "`MILP_Portfolio_Fixgebuehren.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -434,11 +486,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Alle Statusfälle behandeln\n",
+ "\n",
+ "`Solverstatus_und_Gap.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Solverstatus_und_Gap.py\n",
@@ -654,7 +709,36 @@
" print(\" Warmstart_Effekt.py - dort halbiert ein Heuristik-Hinweis die Zeit).\")\n",
" print(\" * Sie planen laufend neu und der gestrige Plan ist fast noch gueltig.\")\n",
" print(\"In beiden Faellen gilt: MESSEN, nicht glauben.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Solverstatus_und_Gap.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Solverstatus_und_Gap.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Solverstatus_und_Gap.py nicht gefunden')"
]
},
{
@@ -667,11 +751,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Warm-Starts: was sie können und was nicht\n",
+ "\n",
+ "`Warmstart_Effekt.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Warmstart_Effekt.py\n",
@@ -819,7 +906,36 @@
" print(\" davon ab, wie schnell der Solver von allein eine vergleichbar gute\")\n",
" print(\" Loesung findet. Das ist die eigentliche Lehre: Ein Warm-Start ist\")\n",
" print(\" eine Messung wert, keine Glaubensfrage.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Warmstart_Effekt.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Warmstart_Effekt.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Warmstart_Effekt.py nicht gefunden')"
]
},
{
@@ -832,11 +948,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Big_M_Falle.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Big_M_Falle.py\n",
@@ -1030,7 +1149,36 @@
" print(\"nicht darauf: Presolve kann das nur, wenn eine implizite Schranke\")\n",
" print(\"herleitbar ist. Die Pruefung aus pruefe() kostet Millisekunden und\")\n",
" print(\"funktioniert immer.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Big_M_Falle.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Big_M_Falle.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Big_M_Falle.py nicht gefunden')"
]
}
],
diff --git a/OR_HTML_04/Notebooks_04/modellierung.ipynb b/OR_HTML_04/Notebooks_04/modellierung.ipynb
index df7ab93..c44b88d 100644
--- a/OR_HTML_04/Notebooks_04/modellierung.ipynb
+++ b/OR_HTML_04/Notebooks_04/modellierung.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Derselbe Datensatz, fünf Modelle\n",
+ "\n",
+ "`Vom_Wunsch_zum_Modell.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/OR_HTML_04/Notebooks_04/oekosystem.ipynb b/OR_HTML_04/Notebooks_04/oekosystem.ipynb
index ae42071..6518796 100644
--- a/OR_HTML_04/Notebooks_04/oekosystem.ipynb
+++ b/OR_HTML_04/Notebooks_04/oekosystem.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die Entscheidung in drei Fragen\n",
+ "\n",
+ "`Solver_Wahl.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -121,11 +155,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Wie die Isolation aussieht, wenn sie tragen soll\n",
+ "\n",
+ "`Ein_System_Vier_Ansaetze.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Ein_System_Vier_Ansaetze.py\n",
@@ -270,7 +307,45 @@
" print(\"Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.\")\n",
" print(\"(Die Zeiten enthalten Prozessstart und Import - sie messen NICHT die\")\n",
" print(\" reine Solverleistung. Die Uebungsaufgabe 'Laufzeitvergleich' trennt beides.)\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Ein_System_Vier_Ansaetze.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Ein_System_Vier_Ansaetze.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Ein_System_Vier_Ansaetze.py nicht gefunden')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die Schichten im Überblick\n",
+ "\n",
+ "`Modellierungsschichten.py`\n"
]
},
{
@@ -470,11 +545,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Vier Stufen an einem Transportproblem\n",
+ "\n",
+ "`Vektorisierte_Modellgenerierung.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Vektorisierte_Modellgenerierung.py\n",
@@ -714,7 +792,36 @@
" print(\" Schreibweise derselben Matrix - Schleife gegen Kronecker-Produkt.\")\n",
" print(\"\\nDie Lesbarkeit von Variante A ist trotzdem viel wert: Fangen Sie dort an,\")\n",
" print(\"und vektorisieren Sie erst, wenn die Messung es verlangt.\")\n",
- " print(\"=\" * 88)"
+ " print(\"=\" * 88)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Vektorisierte_Modellgenerierung.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Vektorisierte_Modellgenerierung.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Vektorisierte_Modellgenerierung.py nicht gefunden')"
]
}
],
diff --git a/OR_HTML_04/Notebooks_04/praxisfallen.ipynb b/OR_HTML_04/Notebooks_04/praxisfallen.ipynb
index 802e3be..9489dd9 100644
--- a/OR_HTML_04/Notebooks_04/praxisfallen.ipynb
+++ b/OR_HTML_04/Notebooks_04/praxisfallen.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,27 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -31,11 +56,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Falle 1 — Infeasibility{idx:Infeasibility}\n",
+ "\n",
+ "`Infeasibility_Diagnose.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Infeasibility_Diagnose.py\n",
@@ -147,7 +175,36 @@
" print(\"\\nDer Anwender bekommt jetzt einen Plan PLUS eine konkrete Ursache -\")\n",
" print(\"und kann handeln: Vertretung von aussen holen, Stunde verlegen,\")\n",
" print(\"oder die Klasse zusammenlegen.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Infeasibility_Diagnose.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Infeasibility_Diagnose.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Infeasibility_Diagnose.py nicht gefunden')"
]
},
{
@@ -160,11 +217,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Falle 2 — Der Black-Box-Effekt\n",
+ "\n",
+ "`Erklaerbarkeit.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Erklaerbarkeit.py\n",
@@ -249,7 +309,45 @@
" print(f\" -> Die Obergrenze blockiert zwar {blockiert}, kostet aber gerade NICHTS:\")\n",
" print(\" Selbst ohne sie waere die Loesung dieselbe.\")\n",
" print(\" Blockierend und kostenrelevant sind zwei verschiedene Dinge.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Erklaerbarkeit.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Erklaerbarkeit.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Erklaerbarkeit.py nicht gefunden')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Constraint Attribution{idx:Constraint Attribution}: welche Bedingung kostet wie viel?\n",
+ "\n",
+ "`Constraint_Attribution.py`\n"
]
},
{
@@ -584,11 +682,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Ein Enum für fünf Bibliotheken\n",
+ "\n",
+ "`or_kern.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# or_kern.py\n",
@@ -1092,7 +1193,36 @@
" print(\"\\nAlle drei scheitern beim EINLESEN - nicht erst beim Loesen und\")\n",
" print(\"schon gar nicht erst im Bericht. Das ist der ganze Zweck der\")\n",
" print(\"Domaenenschicht.\")\n",
- " print(\"=\" * 78)"
+ " print(\"=\" * 78)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'or_kern.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'or_kern.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('or_kern.py nicht gefunden')"
]
},
{
@@ -1105,11 +1235,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Der Solverwechsel in der Praxis\n",
+ "\n",
+ "`Solverwechsel_CPSAT_HiGHS.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Solverwechsel_CPSAT_HiGHS.py\n",
@@ -1433,7 +1566,36 @@
" print(\"gleich teure Loesungen, darf jeder Solver eine andere davon liefern.\")\n",
" print(\"Hier stimmen sie zufaellig ueberein - darauf zu testen waere trotzdem\")\n",
" print(\"ein unzuverlaessiger Test (siehe JobShop_Intervalle.py).\")\n",
- " print(\"=\" * 82)"
+ " print(\"=\" * 82)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Solverwechsel_CPSAT_HiGHS.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Solverwechsel_CPSAT_HiGHS.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Solverwechsel_CPSAT_HiGHS.py nicht gefunden')"
]
},
{
@@ -1446,11 +1608,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Finde den Denkfehler\n",
+ "\n",
+ "`Betriebsueberwachung.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Betriebsueberwachung.py\n",
@@ -1630,7 +1795,36 @@
" print()\n",
" print(\"Die dritte ist die frueheste Warnung: Sie steigt, lange bevor der Gap\")\n",
" print(\"sichtbar wird, und gibt Zeit zum Handeln.\")\n",
- " print(\"=\" * 84)"
+ " print(\"=\" * 84)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Betriebsueberwachung.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Betriebsueberwachung.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Betriebsueberwachung.py nicht gefunden')"
]
}
],
diff --git a/OR_HTML_04/Notebooks_04/prognose.ipynb b/OR_HTML_04/Notebooks_04/prognose.ipynb
index cf10cf6..3f44049 100644
--- a/OR_HTML_04/Notebooks_04/prognose.ipynb
+++ b/OR_HTML_04/Notebooks_04/prognose.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Programm\n",
+ "\n",
+ "`Predict_then_Optimize.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/OR_HTML_04/Notebooks_04/qp-nlp.ipynb b/OR_HTML_04/Notebooks_04/qp-nlp.ipynb
index 92f9c40..21a6fb3 100644
--- a/OR_HTML_04/Notebooks_04/qp-nlp.ipynb
+++ b/OR_HTML_04/Notebooks_04/qp-nlp.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Konvexität: die präzise Aussage\n",
+ "\n",
+ "`QP_Grundlagen.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -102,6 +136,15 @@
"`KKT_Nachweis.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Die vier KKT-Bedingungen\n",
+ "\n",
+ "`KKT_Nachweis.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -229,6 +272,15 @@
"`Entropie_Maximierte_Allokation.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Praxisfall: Entropie-maximierte Kapitalallokation\n",
+ "\n",
+ "`Entropie_Maximierte_Allokation.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -384,6 +436,15 @@
"`Lokale_Optima_Multistart.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Was ein lokales Optimum praktisch bedeutet\n",
+ "\n",
+ "`Lokale_Optima_Multistart.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/supplychain.ipynb b/OR_HTML_04/Notebooks_04/supplychain.ipynb
index b5842f0..e3419cc 100644
--- a/OR_HTML_04/Notebooks_04/supplychain.ipynb
+++ b/OR_HTML_04/Notebooks_04/supplychain.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Das Programm\n",
+ "\n",
+ "`Kraftwerkseinsatz.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
diff --git a/OR_HTML_04/Notebooks_04/testing.ipynb b/OR_HTML_04/Notebooks_04/testing.ipynb
index 7ce275c..37cefa1 100644
--- a/OR_HTML_04/Notebooks_04/testing.ipynb
+++ b/OR_HTML_04/Notebooks_04/testing.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,27 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -31,11 +56,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Die Testsuite\n",
+ "\n",
+ "`test_or_kern.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# test_or_kern.py\n",
@@ -344,7 +372,36 @@
"\n",
"if __name__ == \"__main__\":\n",
" import sys\n",
- " sys.exit(pytest.main([__file__, \"-v\", \"--tb=short\", \"-p\", \"no:cacheprovider\"]))"
+ " sys.exit(pytest.main([__file__, \"-v\", \"--tb=short\", \"-p\", \"no:cacheprovider\"]))\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'test_or_kern.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'test_or_kern.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('test_or_kern.py nicht gefunden')"
]
},
{
@@ -357,11 +414,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Wer testet die Tests?\n",
+ "\n",
+ "`Mutationstest.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Mutationstest.py\n",
@@ -540,7 +600,36 @@
" print(\"Die Quote selbst ist keine Kennzahl fuer ein Dashboard. Zehn von Hand\")\n",
" print(\"gewaehlte Mutationen sind keine Stichprobe aus der Menge aller\")\n",
" print(\"moeglichen Fehler. Was zaehlt, ist die LISTE der Ueberlebenden.\")\n",
- " print(\"=\" * 84)"
+ " print(\"=\" * 84)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Mutationstest.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Mutationstest.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Mutationstest.py nicht gefunden')"
]
},
{
@@ -553,11 +642,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Ein Vergleich, dem man glauben kann\n",
+ "\n",
+ "`Benchmark_Skalierung.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Benchmark_Skalierung.py\n",
@@ -770,7 +862,36 @@
" print(\" * Etwas ueber Ihre Maschine. Diese Zahlen stammen von einer\")\n",
" print(\" anderen. Der Sinn des Programms ist, dass Sie es auf Ihrer\")\n",
" print(\" laufen lassen.\")\n",
- " print(\"=\" * 92)"
+ " print(\"=\" * 92)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Benchmark_Skalierung.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Benchmark_Skalierung.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Benchmark_Skalierung.py nicht gefunden')"
]
},
{
@@ -783,11 +904,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Das Modell als Dienst\n",
+ "\n",
+ "`Optimierungsdienst.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Optimierungsdienst.py\n",
@@ -1102,7 +1226,36 @@
" print()\n",
" print(\"(Es wird hier nicht gebaut - das Buch setzt keine laufende\")\n",
" print(\" Docker-Installation voraus.)\")\n",
- " print(\"=\" * 80)"
+ " print(\"=\" * 80)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Optimierungsdienst.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Optimierungsdienst.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Optimierungsdienst.py nicht gefunden')"
]
}
],
diff --git a/OR_HTML_04/Notebooks_04/unsicherheit.ipynb b/OR_HTML_04/Notebooks_04/unsicherheit.ipynb
index 2e2a12b..0b4ca21 100644
--- a/OR_HTML_04/Notebooks_04/unsicherheit.ipynb
+++ b/OR_HTML_04/Notebooks_04/unsicherheit.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,36 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Der Fluch des Durchschnitts\n",
+ "\n",
+ "`Fluch_des_Durchschnitts.py`\n"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -100,6 +134,15 @@
"`Monte_Carlo.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Monte-Carlo-Simulation{idx:Monte-Carlo-Simulation}\n",
+ "\n",
+ "`Monte_Carlo.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -209,6 +252,15 @@
"`Stochastische_Optimierung.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Zweistufige stochastische Programmierung\n",
+ "\n",
+ "`Stochastische_Optimierung.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -345,6 +397,15 @@
"`Robuste_Optimierung.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Robuste Optimierung{idx:Robuste Optimierung}: gegen den Worst Case absichern\n",
+ "\n",
+ "`Robuste_Optimierung.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -461,6 +522,15 @@
"`Chance_Constraints.py`\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Der Fall: ein Kraftwerkspark mit 500 MW Zusage\n",
+ "\n",
+ "`Chance_Constraints.py`\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/OR_HTML_04/Notebooks_04/vorwort.ipynb b/OR_HTML_04/Notebooks_04/vorwort.ipynb
index 66c47aa..b9a4ced 100644
--- a/OR_HTML_04/Notebooks_04/vorwort.ipynb
+++ b/OR_HTML_04/Notebooks_04/vorwort.ipynb
@@ -12,7 +12,11 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "metadata": {
+ "tags": [
+ "nbmake-skip"
+ ]
+ },
"outputs": [],
"source": [
"# Einmalig ausfuehren: installiert alle im Buch verwendeten Pakete.\n",
@@ -21,6 +25,27 @@
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n",
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n",
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n",
+ "import os, sys\n",
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n",
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n",
+ " sys.path.insert(0, _p); break\n",
+ "import multiprocessing as _mp\n",
+ "try: _mp.set_start_method('fork', force=True)\n",
+ "except (RuntimeError, ValueError): pass"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -31,11 +56,14 @@
]
},
{
- "cell_type": "code",
- "execution_count": null,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
+ "## Installationstest {-}\n",
+ "\n",
+ "`Installationstest.py`\n",
+ "\n",
+ "```python\n",
"#!/usr/bin/env python3\n",
"\n",
"# Installationstest.py\n",
@@ -154,7 +182,36 @@
" print(\"-\" * 78)\n",
" print(\"Alles bereit — Sie können mit dem ersten Kapitel beginnen.\"\n",
" if alle_ok else \"Mindestens ein Solver arbeitet fehlerhaft.\")\n",
- " sys.exit(0 if alle_ok else 1)"
+ " sys.exit(0 if alle_ok else 1)\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# In einem Notebook kann 'Installationstest.py' nicht direkt ausgefuehrt werden\n",
+ "# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n",
+ "# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n",
+ "import subprocess, sys, os\n",
+ "# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n",
+ "# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n",
+ "_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n",
+ "for _d in [os.path.join(os.path.dirname(os.getcwd()),\n",
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n",
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n",
+ " _p = os.path.join(_d, 'Installationstest.py')\n",
+ " if os.path.isfile(_p):\n",
+ " _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n",
+ " capture_output=True, text=True)\n",
+ " if _r.stdout: print(_r.stdout)\n",
+ " if _r.stderr: print(_r.stderr)\n",
+ " _r.check_returncode()\n",
+ " break\n",
+ "else:\n",
+ " print('Installationstest.py nicht gefunden')"
]
}
],
diff --git a/OR_HTML_04/bilder_04/titelseite.svg b/OR_HTML_04/bilder_04/titelseite.svg
index 2872aed..2ed7226 100644
--- a/OR_HTML_04/bilder_04/titelseite.svg
+++ b/OR_HTML_04/bilder_04/titelseite.svg
@@ -2444,7 +2444,7 @@ z
-
+
-
+
-
-
@@ -2580,7 +2616,7 @@ z
-
+
@@ -2600,11 +2636,11 @@ z
-
-
+
+
-
-
+
+
diff --git a/OR_HTML_04/gesamtdokument.html b/OR_HTML_04/gesamtdokument.html
index 3df8e00..23654df 100644
--- a/OR_HTML_04/gesamtdokument.html
+++ b/OR_HTML_04/gesamtdokument.html
@@ -381,7 +381,7 @@
Mit Python planen, optimieren und entscheiden: von Personal, Schichten und Logistik über Energie, Ressourcen und Netzwerke bis zur Portfoliooptimierung an den Finanzmärkten .
Autor / Herausgeber: Dieter Schlüter
<dieter(dot)schlueter(atsign)linix(dot)de>
-Stand: 10. September 2026 v17.50
+Stand: 11. September 2026 v22.37
diff --git a/OR_HTML_04/index.html b/OR_HTML_04/index.html
index 3d7b6d4..ae563e1 100644
--- a/OR_HTML_04/index.html
+++ b/OR_HTML_04/index.html
@@ -115,7 +115,7 @@
Mit Python planen, optimieren und entscheiden: von Personal, Schichten und Logistik über Energie, Ressourcen und Netzwerke bis zur Portfoliooptimierung an den Finanzmärkten.
Autor / Herausgeber: Dieter Schlüter
<dieter(dot)schlueter(atsign)linix(dot)de>
- Stand: 10. September 2026 v17.52
+ Stand: 11. September 2026 v22.38
diff --git a/OR_HTML_04/notebooks.html b/OR_HTML_04/notebooks.html
index 597c410..8336c37 100644
--- a/OR_HTML_04/notebooks.html
+++ b/OR_HTML_04/notebooks.html
@@ -99,7 +99,7 @@
- Notebooks Zu jedem Kapitel gehört ein Jupyter-Notebook mit allen Codeblöcken in Lesereihenfolge — insgesamt 25. Drei Wege, sie auszuführen: in einer lokalen Jupyter -Installation öffnen, in Google Colab hochladen, oder mit dem Kurs-Image starten, das alle Solver mitbringt:
docker run --rm -p 127.0.0.1:8888:8888 or-mit-python Alle 25 Notebooks als ZIP (208 KB)
Kapitel Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung einfuehrung.ipynbKapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität{idx:Konvexität} fundament.ipynbKapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten oekosystem.ipynbKapitel 4: Vom Management-Wunsch zum Modell modellierung.ipynbKapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise lp.ipynbKapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound{idx:Branch-and-Bound} milp.ipynbKapitel 7: Constraint Programming mit CP-SAT{idx:CP-SAT} — Logik, Scheduling und Zuweisung cpsat.ipynbKapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP graphen.ipynbKapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt metaheuristiken.ipynbKapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten dekomposition.ipynbKapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität qp-nlp.ipynbKapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit unsicherheit.ipynbKapitel 13: Dynamische Programmierung — Die Bellman-Gleichung{idx:Bellman-Gleichung} und Order-Execution dynamische-programmierung.ipynbKapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte mehrziel.ipynbKapitel 15: Predict-then-Optimize{idx:Predict-then-Optimize} — die bessere Prognose, die schlechtere Entscheidung prognose.ipynbKapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten bruecke.ipynbKapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit supplychain.ipynbKapitel 18: Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage finanzdaten.ipynbKapitel 19: Die moderne Portfoliotheorie nach Markowitz markowitz.ipynbKapitel 20: Tail-Risiko, CVaR und Transaktionskosten{idx:Transaktionskosten} cvar.ipynbKapitel 21: Die vollständige quantitative Handelsmaschine handelsmaschine.ipynbKapitel 22: Praxisfallen und der Weg zum produktiven Einsatz praxisfallen.ipynbKapitel 23: Testen, Messen, Ausliefern testing.ipynb
+ Notebooks Zu jedem Kapitel gehört ein Jupyter-Notebook mit allen Codeblöcken in Lesereihenfolge — insgesamt 25. Drei Wege, sie auszuführen: in einer lokalen Jupyter -Installation öffnen, in Google Colab hochladen, oder mit dem Kurs-Image starten, das alle Solver mitbringt:
docker run --rm -p 127.0.0.1:8888:8888 or-mit-python Alle 25 Notebooks als ZIP (220 KB)
Kapitel Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung einfuehrung.ipynbKapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität{idx:Konvexität} fundament.ipynbKapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten oekosystem.ipynbKapitel 4: Vom Management-Wunsch zum Modell modellierung.ipynbKapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise lp.ipynbKapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound{idx:Branch-and-Bound} milp.ipynbKapitel 7: Constraint Programming mit CP-SAT{idx:CP-SAT} — Logik, Scheduling und Zuweisung cpsat.ipynbKapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP graphen.ipynbKapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt metaheuristiken.ipynbKapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten dekomposition.ipynbKapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität qp-nlp.ipynbKapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit unsicherheit.ipynbKapitel 13: Dynamische Programmierung — Die Bellman-Gleichung{idx:Bellman-Gleichung} und Order-Execution dynamische-programmierung.ipynbKapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte mehrziel.ipynbKapitel 15: Predict-then-Optimize{idx:Predict-then-Optimize} — die bessere Prognose, die schlechtere Entscheidung prognose.ipynbKapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten bruecke.ipynbKapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit supplychain.ipynbKapitel 18: Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage finanzdaten.ipynbKapitel 19: Die moderne Portfoliotheorie nach Markowitz markowitz.ipynbKapitel 20: Tail-Risiko, CVaR und Transaktionskosten{idx:Transaktionskosten} cvar.ipynbKapitel 21: Die vollständige quantitative Handelsmaschine handelsmaschine.ipynbKapitel 22: Praxisfallen und der Weg zum produktiven Einsatz praxisfallen.ipynbKapitel 23: Testen, Messen, Ausliefern testing.ipynb
diff --git a/OR_HTML_04/vorwort.html b/OR_HTML_04/vorwort.html
index 7507f55..971a36d 100644
--- a/OR_HTML_04/vorwort.html
+++ b/OR_HTML_04/vorwort.html
@@ -110,7 +110,7 @@
Mit Python planen, optimieren und entscheiden: von Personal, Schichten und Logistik über Energie, Ressourcen und Netzwerke bis zur Portfoliooptimierung an den Finanzmärkten .
Autor / Herausgeber: Dieter Schlüter
<dieter(dot)schlueter(atsign)linix(dot)de>
-Stand: 10. September 2026 v17.52
+Stand: 11. September 2026 v22.38
Über den Kurs
Vorwort
diff --git a/Operations_Research_mit_Python_Version_04.md b/Operations_Research_mit_Python_Version_04.md
index c840127..12fa48d 100644
--- a/Operations_Research_mit_Python_Version_04.md
+++ b/Operations_Research_mit_Python_Version_04.md
@@ -26,7 +26,7 @@ Autor / Herausgeber: Dieter Schlüter
-Stand: 10. September 2026 v17.50
+Stand: 11. September 2026 v22.37
---
diff --git a/Operations_Research_mit_Python_Version_04/README.md b/Operations_Research_mit_Python_Version_04/README.md
index 8d4990a..922881e 100644
--- a/Operations_Research_mit_Python_Version_04/README.md
+++ b/Operations_Research_mit_Python_Version_04/README.md
@@ -157,4 +157,4 @@ Details zu den Lernpfaden stehen in [`00_Vorwort_und_Lesehilfe.md`](00_Vorwort_u
---
-*Autor / Herausgeber: Dieter Schlüter · Stand: 10. September 2026 v17.51*
+*Autor / Herausgeber: Dieter Schlüter · Stand: 11. September 2026 v22.38*
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 08fae5a..686463f 100644
--- a/Operations_Research_mit_Python_Version_04/build_version_04.py
+++ b/Operations_Research_mit_Python_Version_04/build_version_04.py
@@ -2293,6 +2293,59 @@ NOTEBOOK_SETUP = (
" scikit-learn matplotlib plotly pyomo linopy pymoo pydantic openpyxl\n"
)
+# Zweite Setup-Zelle — wird NICHT von nbmake übersprungen, damit __file__,
+# sys.path und multiprocessing-Startmodus beim Testen aktiv sind.
+# __file__ : Programme nutzen es für OUTPUT_DIR (s.o.).
+# sys.path : 'from or_kern import …' findet or_kern.py im
+# Programmverzeichnis (lokal und im Docker-Image).
+# multiprocessing : ProcessPoolExecutor (Ein_System_Vier_Ansaetze.py u.a.)
+# braucht den 'fork'-Startmodus, weil der Spawn-Modus
+# die Funktionen in __main__ des Kernels nicht findet.
+NOTEBOOK_SETUP_2 = (
+ "# Notebook-spezifisches Setup: __file__ definieren (Programme nutzen es\n"
+ "# fuer OUTPUT_DIR), Programmverzeichnis in den Suchpfad aufnehmen (or_kern)\n"
+ "# und multiprocessing auf 'fork' stellen (Spawn findet Kernel-Funktionen nicht).\n"
+ "import os, sys\n"
+ "__file__ = os.path.join(os.getcwd(), '_notebook.py')\n"
+ "for _p in [os.path.join(os.path.dirname(os.getcwd()),\n"
+ " 'Operations_Research_mit_Python_Version_04_Programme'),\n"
+ " os.path.dirname(os.getcwd()), os.getcwd()]:\n"
+ " if os.path.isfile(os.path.join(_p, 'or_kern.py')):\n"
+ " sys.path.insert(0, _p); break\n"
+ "import multiprocessing as _mp\n"
+ "try: _mp.set_start_method('fork', force=True)\n"
+ "except (RuntimeError, ValueError): pass\n"
+)
+
+
+def _braucht_subprocess(quelltext: str) -> bool:
+ """True, wenn das Programm in einem Notebook nicht direkt ausfuehrbar ist.
+
+ Gruende:
+ - import highspy: kollidiert mit ortools im selben Kernel (beide bringen eine
+ eigene HiGHS-Kopie mit). Die Programme loesen das durch getrennte Prozesse;
+ im Notebook muss das Subprocess uebernehmen.
+ - import pytest: pytest.main() beendet den Kernel mit SystemExit.
+ - ProcessPoolExecutor mit explizitem 'spawn': der geforkte Prozess findet
+ die Funktionen in __main__ (dem IPython-Kernel) nicht.
+ """
+ if "import highspy" in quelltext or "from highspy" in quelltext:
+ return True
+ if "import pytest" in quelltext or "from pytest" in quelltext:
+ return True
+ if 'get_context("spawn")' in quelltext:
+ return True
+ # Programme, die __file__ nutzen, um andere .py-Dateien zu finden
+ # (z.B. Mutationstest.py sucht or_kern.py und test_or_kern.py im selben
+ # Verzeichnis — im Notebook liegen sie nicht dort).
+ if "import subprocess" in quelltext and "__file__" in quelltext:
+ return True
+ # Programme, die sys.exit() nutzen — im Notebook wirft das SystemExit,
+ # das nbmake als Fehler wertet (auch bei Exit-Code 0).
+ if "sys.exit(" in quelltext:
+ return True
+ return False
+
def _notebook_zellen(text: str, titel: str) -> list[dict]:
"""Baut die Zellenliste eines Kapitel-Notebooks: Titel, Setup-Zelle und
@@ -2303,17 +2356,35 @@ def _notebook_zellen(text: str, titel: str) -> list[dict]:
return {"cell_type": "markdown", "metadata": {},
"source": quelle.splitlines(keepends=True)}
- def code(quelle: str) -> dict:
- return {"cell_type": "code", "execution_count": None, "metadata": {},
- "outputs": [], "source": quelle.rstrip("\n").splitlines(keepends=True)}
+ def code(quelle: str, tags: list[str] | None = None) -> dict:
+ zelle = {"cell_type": "code", "execution_count": None, "metadata": {},
+ "outputs": [], "source": quelle.rstrip("\n").splitlines(keepends=True)}
+ if tags:
+ zelle["metadata"]["tags"] = tags
+ return zelle
zellen = [
md(f"# {titel}\n\n"
f"Begleitnotebook zu *{SITE_TITEL}*. Die Codezellen sind identisch "
f"mit den im Buch abgedruckten Programmen.\n"),
- code(NOTEBOOK_SETUP),
+ # Setup-Zelle per Tag markieren, damit nbmake sie beim Testen
+ # überspringt - sonst wuerde jeder Testlauf die Pakete neu
+ # installieren (langsam und in der CI netzabhaengig).
+ code(NOTEBOOK_SETUP, tags=["nbmake-skip"]),
+ # Zweite Setup-Zelle (wird beim Testen ausgefuehrt): __file__,
+ # sys.path und multiprocessing-Startmodus.
+ code(NOTEBOOK_SETUP_2),
]
+ # Pre-Scan: enthaelt das Kapitel sowohl ortools als auch highspy?
+ # Dann beiessen sich die Imports im selben Kernel — alle Solver-Programme
+ # muessen als Subprocess laufen, nicht nur die highspy-Importe.
+ hat_ortools = any("import ortools" in t.group(1) or "from ortools" in t.group(1)
+ for t in CODEBLOCK_RE.finditer(text))
+ hat_highspy = any("import highspy" in t.group(1) or "from highspy" in t.group(1)
+ for t in CODEBLOCK_RE.finditer(text))
+ solver_konflikt = hat_ortools and hat_highspy
+
# Zu jedem Codeblock die letzte Ueberschrift davor merken, damit die
# Markdown-Zelle sagt, wo im Kapitel man sich befindet.
ueberschriften = [(m.start(), m.group(1)) for m in NOTEBOOK_UEBERSCHRIFT_RE.finditer(text)]
@@ -2334,7 +2405,43 @@ def _notebook_zellen(text: str, titel: str) -> list[dict]:
if programm:
kopf += f"\n`{programm.group(1)}`\n"
zellen.append(md(kopf or "---\n"))
- zellen.append(code(quelltext))
+ braucht_sub = _braucht_subprocess(quelltext)
+ # Bei Solver-Konflikt im Kapitel: auch reine ortools-Programme als
+ # Subprocess, weil highspy schon im Kernel geladen sein kann.
+ if solver_konflikt and ("import ortools" in quelltext or "from ortools" in quelltext):
+ braucht_sub = True
+ if braucht_sub:
+ # Code als Markdown-Codeblock zeigen — sichtbar für den Leser,
+ # aber nicht ausführbar (nbmake überspringt Markdown-Zellen).
+ code_block = f"```python\n{quelltext.rstrip()}\n```\n"
+ zellen.append(md((kopf or "---\n") + "\n" + code_block))
+ programm_name = programm.group(1) if programm else "programm.py"
+ subproc = (
+ f"# In einem Notebook kann '{programm_name}' nicht direkt ausgefuehrt werden\n"
+ f"# (ortools/highspy-Konflikt im selben Kernel, pytest oder multiprocessing).\n"
+ f"# Deshalb als Subprocess — so laeuft es genauso wie im Terminal.\n"
+ f"import subprocess, sys, os\n"
+ f"# NO_COLOR/TERM=dumb verhindern ANSI-Codes in der Ausgabe (pytest\n"
+ f"# im Jupyter-Kernel wuerde sonst farbige Escape-Sequenzen ausgeben).\n"
+ f"_env = dict(os.environ, NO_COLOR='1', TERM='dumb')\n"
+ f"for _d in [os.path.join(os.path.dirname(os.getcwd()),\n"
+ f" 'Operations_Research_mit_Python_Version_04_Programme'),\n"
+ f" os.path.dirname(os.getcwd()), os.getcwd()]:\n"
+ f" _p = os.path.join(_d, '{programm_name}')\n"
+ f" if os.path.isfile(_p):\n"
+ f" _r = subprocess.run([sys.executable, _p], cwd=_d, env=_env,\n"
+ f" capture_output=True, text=True)\n"
+ f" if _r.stdout: print(_r.stdout)\n"
+ f" if _r.stderr: print(_r.stderr)\n"
+ f" _r.check_returncode()\n"
+ f" break\n"
+ f"else:\n"
+ f" print('{programm_name} nicht gefunden')\n"
+ )
+ zellen.append(code(subproc))
+ else:
+ zellen.append(md(kopf or "---\n"))
+ zellen.append(code(quelltext))
return zellen
@@ -2363,7 +2470,7 @@ def baue_notebooks(struktur: list[dict]) -> dict[str, str]:
titel = (f'{eintrag["nummer"]}: {eintrag["titel"]}'
if eintrag["nummer"] else eintrag["titel"])
zellen = _notebook_zellen(text, titel)
- if len(zellen) <= 2: # nur Titel + Setup => kein Code im Kapitel
+ if len(zellen) <= 3: # nur Titel + 2 Setup-Zellen => kein Code im Kapitel
continue
name = eintrag["seite"].replace(".html", ".ipynb")
diff --git a/Operations_Research_mit_Python_Version_04/teste_code_04.py b/Operations_Research_mit_Python_Version_04/teste_code_04.py
new file mode 100644
index 0000000..8984203
--- /dev/null
+++ b/Operations_Research_mit_Python_Version_04/teste_code_04.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python3
+# teste_code_04.py
+"""
+Inkrementeller Test der Programme und Notebooks.
+
+Testet nur, was sich seit dem letzten Commit geaendert hat oder neu
+hinzugekommen ist. Netzabhaengige Dateien (yfinance-Downloads) werden
+automatisch erkannt und nur getestet, wenn Internet verfuegbar ist;
+scheitern sie am Netz, gilt das nicht als Fehler.
+
+Aufruf:
+ python3 teste_code_04.py # nur geaenderte/neue
+ python3 teste_code_04.py --alles # alle Programme und Notebooks
+ python3 teste_code_04.py --check # nur anzeigen, was getestet wuerde
+
+Voraussetzung: pip install -e ".[dev,notebook]"
+"""
+
+import argparse
+import json
+import os
+import subprocess
+import sys
+
+HIER = os.path.dirname(os.path.abspath(__file__))
+BASIS = os.path.dirname(HIER)
+PROG_DIR = os.path.join(BASIS, "Operations_Research_mit_Python_Version_04_Programme")
+NB_DIR = os.path.join(BASIS, "Notebooks_04")
+TIMEOUT = 900 # 15 Minuten pro Programm/Notebook (Solver können dauern)
+
+
+# --- Erkennung netzabhaengiger Dateien ---------------------------------------
+
+def netz_abhaengig(datei: str) -> bool:
+ """True, wenn die Datei yfinance importiert (Programm oder Notebook)."""
+ try:
+ with open(datei, encoding="utf-8") as f:
+ inhalt = f.read()
+ except OSError:
+ return False
+ return "import yfinance" in inhalt or "from yfinance" in inhalt
+
+
+def internet_verfuegbar() -> bool:
+ """Prüft, ob yfinance tatsächlich Daten laden kann (nicht nur Port-Erreichbarkeit)."""
+ try:
+ import yfinance as yf
+ h = yf.Ticker("AAPL").history(period="5d")
+ return len(h) > 0
+ except Exception:
+ return False
+
+
+# --- Geaenderte Dateien via Git ermitteln ------------------------------------
+
+def geaenderte_dateien() -> tuple[set[str], set[str]]:
+ """Liefert (geänderte Programme, geänderte Notebooks) relativ zu BASIS.
+
+ 'Geändert' = modifiziert oder neu (untracked) gegenüber HEAD.
+ """
+ # --porcelain liefert zeilenweise 'XY datei'; X=Y=?? bedeutet untracked.
+ ergebnis = subprocess.run(
+ ["git", "status", "--porcelain"],
+ cwd=BASIS, capture_output=True, text=True, check=True,
+ )
+ programme: set[str] = set()
+ notebooks: set[str] = set()
+ for zeile in ergebnis.stdout.splitlines():
+ status, datei = zeile[:2], zeile[3:]
+ # Umbenennungen werden als 'XY alt -> neu' geschrieben.
+ if " -> " in datei:
+ datei = datei.split(" -> ", 1)[1]
+ if datei.startswith("Operations_Research_mit_Python_Version_04_Programme/") \
+ and datei.endswith(".py"):
+ programme.add(os.path.basename(datei))
+ elif datei.startswith("Notebooks_04/") and datei.endswith(".ipynb"):
+ notebooks.add(os.path.basename(datei))
+ return programme, notebooks
+
+
+def alle_dateien() -> tuple[list[str], list[str]]:
+ """Alle vorhandenen Programme und Notebooks."""
+ programme = sorted(
+ f for f in os.listdir(PROG_DIR) if f.endswith(".py") and f != "or_kern.py"
+ )
+ notebooks = sorted(f for f in os.listdir(NB_DIR) if f.endswith(".ipynb"))
+ return programme, notebooks
+
+
+# --- Tests -------------------------------------------------------------------
+
+def test_programm(name: str, netz_ok: bool) -> str:
+ """Führt ein Programm aus. Rückgabe: 'ok', 'fehler', 'netz-übersprungen'."""
+ pfad = os.path.join(PROG_DIR, name)
+ if netz_abhaengig(pfad) and not netz_ok:
+ print(f" {name}: ÜBERSPRUNGEN (braucht Internet, nicht verfügbar)")
+ return "netz"
+ print(f" {name}: läuft …")
+ umgebung = dict(os.environ, MPLBACKEND="Agg")
+ try:
+ subprocess.run(
+ [sys.executable, pfad],
+ env=umgebung, timeout=TIMEOUT,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ check=True,
+ )
+ print(f" {name}: OK")
+ return "ok"
+ except subprocess.TimeoutExpired:
+ print(f" {name}: FEHLER (Timeout nach {TIMEOUT}s)")
+ return "fehler"
+ except subprocess.CalledProcessError as e:
+ ausgabe = (e.stderr or e.stdout or b"").decode("utf-8", errors="replace")
+ # Netzfehler bei einem netzabhängigen Programm → kein harter Fehler
+ if netz_abhaengig(pfad) and "Download fehlgeschlagen" in ausgabe:
+ print(f" {name}: ÜBERSPRUNGEN (Download fehlgeschlagen — kein Internet)")
+ return "netz"
+ print(f" {name}: FEHLER")
+ for zeile in ausgabe.splitlines()[-15:]:
+ print(f" {zeile}")
+ return "fehler"
+
+
+def test_notebooks(names: list[str], netz_ok: bool) -> int:
+ """Führt Notebooks via pytest --nbmake aus. Rückgabe: Anzahl Fehler."""
+ if not names:
+ return 0
+ # Netzabhängige Notebooks ohne Internet herausfiltern
+ testen: list[str] = []
+ for name in names:
+ pfad = os.path.join(NB_DIR, name)
+ if netz_abhaengig(pfad) and not netz_ok:
+ print(f" {name}: ÜBERSPRUNGEN (braucht Internet, nicht verfügbar)")
+ else:
+ testen.append(pfad)
+ if not testen:
+ return 0
+ umgebung = dict(os.environ, MPLBACKEND="Agg")
+ cmd = [sys.executable, "-m", "pytest", "--nbmake",
+ f"--nbmake-timeout={TIMEOUT}", "-q", *testen]
+ print(f" pytest --nbmake für {len(testen)} Notebook(s) …")
+ ergebnis = subprocess.run(cmd, env=umgebung, cwd=BASIS)
+ return 0 if ergebnis.returncode == 0 else 1
+
+
+# --- Hauptprogramm -----------------------------------------------------------
+
+def main() -> int:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("--alles", action="store_true",
+ help="Alle Programme und Notebooks testen, nicht nur geänderte.")
+ parser.add_argument("--check", action="store_true",
+ help="Nur anzeigen, was getestet würde, nichts ausführen.")
+ args = parser.parse_args()
+
+ print("=== Inkrementeller Test der Programme und Notebooks ===\n")
+
+ # 1. Programme und Notebooks regenerieren
+ print("1. Programme extrahieren …")
+ subprocess.run(
+ [sys.executable, os.path.join(HIER, "extract_programme_04.py")],
+ cwd=BASIS, check=True,
+ )
+ print(" Notebooks bauen …")
+ subprocess.run(
+ [sys.executable, os.path.join(HIER, "build_version_04.py"), "--html"],
+ cwd=BASIS, capture_output=True, check=True,
+ )
+
+ # 2. Geänderte Dateien ermitteln
+ if args.alles:
+ programme, notebooks = alle_dateien()
+ modus = "alle"
+ else:
+ programme_set, notebooks_set = geaenderte_dateien()
+ programme = sorted(programme_set)
+ notebooks = sorted(notebooks_set)
+ modus = "geänderte/neue"
+
+ # or_kern.py ist eine Bibliothek, kein Programm — nie direkt ausführen
+ if "or_kern.py" in programme:
+ programme.remove("or_kern.py")
+
+ print(f"\n2. {modus.capitalize()} Dateien: "
+ f"{len(programme)} Programm(e), {len(notebooks)} Notebook(s)")
+
+ if not programme and not notebooks:
+ print(" Nichts zu testen — alles aktuell.")
+ return 0
+
+ if programme:
+ print(" Programme:")
+ for p in programme:
+ print(f" {p}")
+ if notebooks:
+ print(" Notebooks:")
+ for nb in notebooks:
+ print(f" {nb}")
+
+ if args.check:
+ print("\n--check: Nur Anzeige, kein Testlauf.")
+ return 0
+
+ # 3. Internet-Check
+ netz_ok = internet_verfuegbar()
+ print(f"\n3. Internet: {'verfügbar' if netz_ok else 'nicht verfügbar'}"
+ f" (yfinance-Programme werden "
+ f"{'getestet' if netz_ok else 'übersprungen'})")
+
+ # 4. Programme testen
+ fehler_zaehler = 0
+ if programme:
+ print("\n4. Programme testen:")
+ for p in programme:
+ ergebnis = test_programm(p, netz_ok)
+ if ergebnis == "fehler":
+ fehler_zaehler += 1
+
+ # 5. Notebooks testen
+ if notebooks:
+ print("\n5. Notebooks testen:")
+ nb_fehler = test_notebooks(notebooks, netz_ok)
+ fehler_zaehler += nb_fehler
+
+ # 6. Zusammenfassung
+ print(f"\n=== Zusammenfassung ===")
+ if fehler_zaehler > 0:
+ print(f" {fehler_zaehler} Fehler insgesamt.")
+ return 1
+ print(" Alle getesteten Programme und Notebooks fehlerfrei.")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
\ No newline at end of file
diff --git a/PLAN.md b/PLAN.md
index 576a0a6..ad67737 100644
--- a/PLAN.md
+++ b/PLAN.md
@@ -382,10 +382,16 @@ MPLBACKEND=Agg bash -c 'for p in Operations_Research_mit_Python_Version_04_Progr
# (liegt flach im Programme-Verzeichnis, nicht in einem tests/-Unterordner)
pytest Operations_Research_mit_Python_Version_04_Programme/test_or_kern.py -q
-# 4. PDF und Website
+# 4. Notebooks zelle-für-zelle ausführen
+# Die %pip-Setup-Zelle trägt das Tag 'nbmake-skip' und wird vom Build
+# automatisch markiert – nbmake überspringt sie, damit kein Testlauf die
+# Pakete neu installiert. Voraussetzung: pip install -e ".[dev,notebook]".
+pytest --nbmake --nbmake-timeout=900 Notebooks_04/ -q
+
+# 5. PDF und Website
python3 Operations_Research_mit_Python_Version_04/build_version_04.py --pdf --html
-# 5. Gegenprobe: Version 03 muss unverändert bauen
+# 6. Gegenprobe: Version 03 muss unverändert bauen
python3 Operations_Research_mit_Python_Version_03/build_version_03.py --check
```
diff --git a/PROGRESS.md b/PROGRESS.md
index f5b3f02..e0966b5 100644
--- a/PROGRESS.md
+++ b/PROGRESS.md
@@ -7,6 +7,12 @@ angefallen sind. Wer nach einer Unterbrechung weitermacht, liest diese Datei zue
**Stand:** 7. September 2026 · Branch `main` · **Phasen 0–5 abgeschlossen**
+*Nachträglich ergänzt (gleicher Monat):* Notebook-Test-Infrastruktur — `nbmake` in
+`pyproject.toml`, zweite Setup-Zelle in den Notebooks (`__file__`, `sys.path`,
+multiprocessing-`fork`), Subprocess-Zellen für Programme, die nicht direkt im Kernel
+laufen (Solver-Konflikt, pytest, `sys.exit`), und `teste_code_04.py` für inkrementelle
+Tests. Alle 25 Notebooks laufen fehlerfrei (`25 passed`).
+
*(Die genaue Position in der Historie liefert `git log --oneline`; ein Hash steht hier
bewusst nicht, weil er sich mit genau dem Commit ändert, der diese Zeile enthält.)*
@@ -37,7 +43,12 @@ python3 Operations_Research_mit_Python_Version_04/build_version_04.py --check
python3 Operations_Research_mit_Python_Version_04/extract_programme_04.py
MPLBACKEND=Agg bash -c 'for p in Operations_Research_mit_Python_Version_04_Programme/*.py; do
timeout 600 python3 "$p" >/dev/null 2>&1 || echo "FEHLER: $p"; done'
-# Soll: 73 Programme, keine FEHLER-Zeile.
+# Soll: 76 Programme, keine FEHLER-Zeile (die 4 yfinance-Programme brauchen
+# Internet — ohne Internet scheitern sie am Download, nicht am Code).
+
+# 3b. Laufen alle Notebooks? (ca. 13 Minuten; braucht pip install -e ".[dev,notebook]")
+pytest --nbmake --nbmake-timeout=900 Notebooks_04/ -q
+# Soll: 25 passed. Alternativ: python3 .../teste_code_04.py (nur geänderte).
# 4. Gegenprobe: Version 03 ist unberührt (liegt seit der Trennung daneben)
python3 ../Version_03/Operations_Research_mit_Python_Version_03/build_version_03.py --check
diff --git a/bilder_04/titelseite.svg b/bilder_04/titelseite.svg
index 2872aed..2ed7226 100644
--- a/bilder_04/titelseite.svg
+++ b/bilder_04/titelseite.svg
@@ -2444,7 +2444,7 @@ z
-
+
-
+
-
-
@@ -2580,7 +2616,7 @@ z
-
+
@@ -2600,11 +2636,11 @@ z
-
-
+
+
-
-
+
+
diff --git a/pyproject.toml b/pyproject.toml
index af6e038..32b437b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -90,6 +90,7 @@ figures = [
dev = [
"pytest>=8.0",
+ "nbmake>=1.5",
]
# Die 25 Kapitel-Notebooks im Browser ausfuehren - ohne dass jemand etwas