Stichwortregister: automatische Markierung aller Glossarbegriffe (988 statt 456 Marker)

Neues Skript erzeuge_stichwortregister_04.py:
- Sucht alle Glossarbegriffe (Anzeigename) in den Kapiteldateien
  und markiert die erste Fundstelle je Datei mit {idx:Indexmarke}
- Termliste aus glossar_eintraege_04.py + vorhandenen {idx:...}-Markern
- Idempotent: erkennt vorhandene Marker und überspringt sie
- Überspringt Codeblöcke, Inline-Code und Math
- --check und --bericht Modi
- 605 Marker eingefügt (2 Durchläufe: 597 + 8)

Zusätzlich:
- titeltexte_04.py _saeubern(): entfernt {idx:...}-Marker vor
  Titeltext-Vergleich (pruefe_titeltexte schlug sonst fehl)
This commit is contained in:
dschlueter 2026-09-10 00:07:01 +02:00
commit 99cb85907c
107 changed files with 1529 additions and 1214 deletions

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung\n",
"# Kapitel 7: Constraint Programming mit CP-SAT{idx:CP-SAT} — Logik, Scheduling und Zuweisung\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 20: Tail-Risiko, CVaR und Transaktionskosten\n",
"# Kapitel 20: Tail-Risiko, CVaR und Transaktionskosten{idx:Transaktionskosten}\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution\n",
"# Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung{idx:Bellman-Gleichung} und Order-Execution\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]

View file

@ -25,7 +25,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Logarithmische Rendite\n",
"## Logarithmische Rendite{idx:Logarithmische Rendite}\n",
"\n",
"`Renditen_Vergleich.py`\n"
]
@ -110,7 +110,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Das Schätzfehler-Problem\n",
"## Das Schätzfehler{idx:Schätzfehler}-Problem\n",
"\n",
"`Schaetzrauschen_Demo.py`\n"
]

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität\n",
"# Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität{idx:Konvexität}\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]

View file

@ -139,7 +139,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Der Satz von Birkhoff und von Neumann\n",
"## Der Satz von Birkhoff und von Neumann{idx:Satz von Birkhoff und von Neumann}\n",
"\n",
"`Zuordnung_Ungarisch.py`\n"
]

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound\n",
"# Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound{idx:Branch-and-Bound}\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]
@ -133,7 +133,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Beispiel: Das Rucksackproblem\n",
"## Beispiel: Das Rucksackproblem{idx:Rucksackproblem}\n",
"\n",
"`Rucksack.py`\n"
]

View file

@ -25,7 +25,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Falle 1 — Infeasibility\n",
"## Falle 1 — Infeasibility{idx:Infeasibility}\n",
"\n",
"`Infeasibility_Diagnose.py`\n"
]
@ -256,7 +256,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraint Attribution: welche Bedingung kostet wie viel?\n",
"## Constraint Attribution{idx:Constraint Attribution}: welche Bedingung kostet wie viel?\n",
"\n",
"`Constraint_Attribution.py`\n"
]

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 15: Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung\n",
"# Kapitel 15: Predict-then-Optimize{idx:Predict-then-Optimize} — die bessere Prognose, die schlechtere Entscheidung\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]

View file

@ -95,7 +95,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Monte-Carlo-Simulation\n",
"## Monte-Carlo-Simulation{idx:Monte-Carlo-Simulation}\n",
"\n",
"`Monte_Carlo.py`\n"
]
@ -340,7 +340,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Robuste Optimierung: gegen den Worst Case absichern\n",
"## Robuste Optimierung{idx:Robuste Optimierung}: gegen den Worst Case absichern\n",
"\n",
"`Robuste_Optimierung.py`\n"
]

Binary file not shown.

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung\n",
"# Kapitel 7: Constraint Programming mit CP-SAT{idx:CP-SAT} — Logik, Scheduling und Zuweisung\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 20: Tail-Risiko, CVaR und Transaktionskosten\n",
"# Kapitel 20: Tail-Risiko, CVaR und Transaktionskosten{idx:Transaktionskosten}\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution\n",
"# Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung{idx:Bellman-Gleichung} und Order-Execution\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]

View file

@ -25,7 +25,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Logarithmische Rendite\n",
"## Logarithmische Rendite{idx:Logarithmische Rendite}\n",
"\n",
"`Renditen_Vergleich.py`\n"
]
@ -110,7 +110,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Das Schätzfehler-Problem\n",
"## Das Schätzfehler{idx:Schätzfehler}-Problem\n",
"\n",
"`Schaetzrauschen_Demo.py`\n"
]

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität\n",
"# Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität{idx:Konvexität}\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]

View file

@ -139,7 +139,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Der Satz von Birkhoff und von Neumann\n",
"## Der Satz von Birkhoff und von Neumann{idx:Satz von Birkhoff und von Neumann}\n",
"\n",
"`Zuordnung_Ungarisch.py`\n"
]

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound\n",
"# Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound{idx:Branch-and-Bound}\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]
@ -133,7 +133,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Beispiel: Das Rucksackproblem\n",
"## Beispiel: Das Rucksackproblem{idx:Rucksackproblem}\n",
"\n",
"`Rucksack.py`\n"
]

View file

@ -25,7 +25,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Falle 1 — Infeasibility\n",
"## Falle 1 — Infeasibility{idx:Infeasibility}\n",
"\n",
"`Infeasibility_Diagnose.py`\n"
]
@ -256,7 +256,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraint Attribution: welche Bedingung kostet wie viel?\n",
"## Constraint Attribution{idx:Constraint Attribution}: welche Bedingung kostet wie viel?\n",
"\n",
"`Constraint_Attribution.py`\n"
]

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 15: Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung\n",
"# Kapitel 15: Predict-then-Optimize{idx:Predict-then-Optimize} — die bessere Prognose, die schlechtere Entscheidung\n",
"\n",
"Begleitnotebook zu *Optimierte Entscheidungsfindung mit Python*. Die Codezellen sind identisch mit den im Buch abgedruckten Programmen.\n"
]

View file

@ -95,7 +95,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Monte-Carlo-Simulation\n",
"## Monte-Carlo-Simulation{idx:Monte-Carlo-Simulation}\n",
"\n",
"`Monte_Carlo.py`\n"
]
@ -340,7 +340,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Robuste Optimierung: gegen den Worst Case absichern\n",
"## Robuste Optimierung{idx:Robuste Optimierung}: gegen den Worst Case absichern\n",
"\n",
"`Robuste_Optimierung.py`\n"
]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -2444,37 +2444,42 @@ z
</g>
</g>
<g id="text_14">
<!-- Stand: 9. September 2026 -->
<!-- Stand: 10. September 2026 -->
<g style="fill: #c7d2fe" transform="translate(71.4528 818.05749) scale(0.12 -0.12)">
<defs>
<path id="DejaVuSans-39" d="M 703 97
L 703 672
Q 941 559 1184 500
Q 1428 441 1663 441
Q 2288 441 2617 861
Q 2947 1281 2994 2138
Q 2813 1869 2534 1725
Q 2256 1581 1919 1581
Q 1219 1581 811 2004
Q 403 2428 403 3163
Q 403 3881 828 4315
Q 1253 4750 1959 4750
Q 2769 4750 3195 4129
Q 3622 3509 3622 2328
Q 3622 1225 3098 567
Q 2575 -91 1691 -91
Q 1453 -91 1209 -44
Q 966 3 703 97
<path id="DejaVuSans-31" d="M 794 531
L 1825 531
L 1825 4091
L 703 3866
L 703 4441
L 1819 4666
L 2450 4666
L 2450 531
L 3481 531
L 3481 0
L 794 0
L 794 531
z
M 1959 2075
Q 2384 2075 2632 2365
Q 2881 2656 2881 3163
Q 2881 3666 2632 3958
Q 2384 4250 1959 4250
Q 1534 4250 1286 3958
Q 1038 3666 1038 3163
Q 1038 2656 1286 2365
Q 1534 2075 1959 2075
" transform="scale(0.015625)"/>
<path id="DejaVuSans-30" d="M 2034 4250
Q 1547 4250 1301 3770
Q 1056 3291 1056 2328
Q 1056 1369 1301 889
Q 1547 409 2034 409
Q 2525 409 2770 889
Q 3016 1369 3016 2328
Q 3016 3291 2770 3770
Q 2525 4250 2034 4250
z
M 2034 4750
Q 2819 4750 3233 4129
Q 3647 3509 3647 2328
Q 3647 1150 3233 529
Q 2819 -91 2034 -91
Q 1250 -91 836 529
Q 422 1150 422 2328
Q 422 3509 836 4129
Q 1250 4750 2034 4750
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-32" d="M 1228 531
@ -2500,27 +2505,6 @@ Q 3191 2616 2906 2266
Q 2828 2175 2409 1742
Q 1991 1309 1228 531
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-30" d="M 2034 4250
Q 1547 4250 1301 3770
Q 1056 3291 1056 2328
Q 1056 1369 1301 889
Q 1547 409 2034 409
Q 2525 409 2770 889
Q 3016 1369 3016 2328
Q 3016 3291 2770 3770
Q 2525 4250 2034 4250
z
M 2034 4750
Q 2819 4750 3233 4129
Q 3647 3509 3647 2328
Q 3647 1150 3233 529
Q 2819 -91 2034 -91
Q 1250 -91 836 529
Q 422 1150 422 2328
Q 422 3509 836 4129
Q 1250 4750 2034 4750
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-36" d="M 2113 2584
Q 1688 2584 1439 2293
@ -2560,23 +2544,24 @@ z
<use xlink:href="#DejaVuSans-64" transform="translate(227.34375 0)"/>
<use xlink:href="#DejaVuSans-3a" transform="translate(290.820312 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(324.511719 0)"/>
<use xlink:href="#DejaVuSans-39" transform="translate(356.298828 0)"/>
<use xlink:href="#DejaVuSans-2e" transform="translate(419.921875 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(451.708984 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(483.496094 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(546.972656 0)"/>
<use xlink:href="#DejaVuSans-70" transform="translate(608.496094 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(671.972656 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(711.181641 0)"/>
<use xlink:href="#DejaVuSans-6d" transform="translate(772.705078 0)"/>
<use xlink:href="#DejaVuSans-62" transform="translate(870.117188 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(933.59375 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(995.117188 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1036.230469 0)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(1068.017578 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(1131.640625 0)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(1195.263672 0)"/>
<use xlink:href="#DejaVuSans-36" transform="translate(1258.886719 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(356.298828 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(419.921875 0)"/>
<use xlink:href="#DejaVuSans-2e" transform="translate(483.544922 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(515.332031 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(547.119141 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(610.595703 0)"/>
<use xlink:href="#DejaVuSans-70" transform="translate(672.119141 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(735.595703 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(774.804688 0)"/>
<use xlink:href="#DejaVuSans-6d" transform="translate(836.328125 0)"/>
<use xlink:href="#DejaVuSans-62" transform="translate(933.740234 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(997.216797 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(1058.740234 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1099.853516 0)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(1131.640625 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(1195.263672 0)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(1258.886719 0)"/>
<use xlink:href="#DejaVuSans-36" transform="translate(1322.509766 0)"/>
</g>
</g>
<g id="text_15">

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

10
OR_HTML_04/cpsat.html generated

File diff suppressed because one or more lines are too long

6
OR_HTML_04/cvar.html generated

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -381,7 +381,7 @@
<p>Mit Python planen, optimieren und entscheiden: von <strong>Personal, Schichten und Logistik</strong> über <strong>Energie, Ressourcen und Netzwerke</strong> bis zur <strong>Portfoliooptimierung an den Finanzmärkten</strong>.</p>
<p>Autor / Herausgeber: Dieter Schlüter</p>
<p>&lt;dieter(dot)schlueter(atsign)linix(dot)de&gt;</p>
<p>Stand: 9. September 2026</p>
<p>Stand: 10. September 2026</p>
<hr />
<nav id="TOC" role="doc-toc">
<ul>
@ -16655,7 +16655,7 @@ DCP-konvex (CVXPY-Pruefung)? False
<p><span class="math display">
\min_{\mathbf{w}}\quad \underbrace{\tfrac12\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}_{\text{Risiko}}
\;-\;\underbrace{\alpha\,\boldsymbol{\mu}^\top\mathbf{w}}_{\text{Ertrag}}
\;-\;\underbrace{\beta\Big(-\textstyle\sum_i w_i\ln w_i\Big)}_{\text{Diversifikation}}
\;-\;\underbrace{\beta\Big(-\textstyle\sum_i w_i\ln w_i\Big)}_{\text{Diversifikation`\index{Diversifikation}`{=latex}}}
</span></p>
<blockquote>
<p><strong>📐 Formel-Lesehilfe zur Entropie</strong> <span class="math inline">H(\mathbf{w}) = -\sum_i w_i \ln w_i</span> ist ein Streuungsmaß: * Alles in einem Titel (<span class="math inline">w = (1,0,0,0)</span>): <span class="math inline">H = 0</span> — minimale Streuung. * Gleichverteilt (<span class="math inline">w_i = 1/4</span>): <span class="math inline">H = \ln 4 \approx 1{,}386</span> — maximale Streuung.</p>
@ -19985,7 +19985,7 @@ e(x) \le \varepsilon
<h2 id="sec:mehrziel-vorlegen">14.8 Wie man die Front vorlegt</h2>
<p>Eine Front mit zehn Punkten ist kein Ergebnis, sondern eine Speisekarte. Damit sie entscheidbar wird, gehört eine Spalte dazu, die im Programm <code>EUR je kg</code> heißt:</p>
<p><span class="math display">
\text{Schattenpreis} = \frac{\text{Aufpreis gegenüber dem Kostenminimum}}
\text{Schattenpreis`\index{Schattenpreis}`{=latex}} = \frac{\text{Aufpreis gegenüber dem Kostenminimum}}
{\text{eingesparte Kilogramm}}
</span></p>
<p>Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem internen CO₂-Preis oder dem Zertifikatspreis. Liegt der Schattenpreis darunter, ist die Entscheidung kaufmännisch schon getroffen.</p>
@ -26173,6 +26173,11 @@ von allen Fehlversuchen genau einen: den erfolgreichsten.
<h3 id="falle-5-laufzeitexplosion">Falle 5 — Laufzeitexplosion</h3>
<p>Exakte Optimalität kann bei MILP oder CP-SAT Minuten bis Stunden dauern — und die Laufzeit wächst nicht linear mit der Problemgröße.</p>
<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 33%" />
<col style="width: 33%" />
</colgroup>
<thead>
<tr class="header">
<th>Einsatzszenario</th>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

6
OR_HTML_04/index.html generated

File diff suppressed because one or more lines are too long

6
OR_HTML_04/lp.html generated

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

10
OR_HTML_04/milp.html generated

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -19,7 +19,7 @@
## Von der mathematischen Modellierung zu praktischen intelligenten Lösungen für Betrieb, Technik und Finanzmärkte {.unlisted .unnumbered}
**Ein praxisorientiertes Kompendium für Entscheider** mit Constraint Programming, Vektor-
und Matrixmethoden sowie HiGHS, SciPy, Google OR-Tools und CVXPY.
und Matrixmethoden sowie HiGHS{idx:HiGHS}, SciPy, Google OR-Tools{idx:OR-Tools} und CVXPY.
Mit Python planen, optimieren und entscheiden: von **Personal, Schichten und Logistik** über **Energie, Ressourcen und Netzwerke** bis zur **Portfoliooptimierung an den Finanzmärkten**.
@ -83,8 +83,8 @@ sowie Mathematik auf Grundstudiumsniveau:
| Gebiet | Was Sie können sollten | Wo im Buch gebraucht |
| --- | --- | --- |
| **Lineare Algebra** | Vektoren, Matrizen, Matrix-Vektor-Produkt, Eigenwerte | ab {ref:kap:fundament}, zentral in {ref:kap:qp-nlp}, {ref:kap:finanzdaten}, {ref:kap:markowitz} |
| **Analysis** | Ableitung, Gradient, notwendige Bedingung erster Ordnung | {ref:kap:qp-nlp} |
| **Lineare Algebra** | Vektoren, Matrizen, Matrix-Vektor-Produkt{idx:Matrix-Vektor-Produkt}, Eigenwerte | ab {ref:kap:fundament}, zentral in {ref:kap:qp-nlp}, {ref:kap:finanzdaten}, {ref:kap:markowitz} |
| **Analysis** | Ableitung, Gradient{idx:Gradient}, notwendige Bedingung erster Ordnung | {ref:kap:qp-nlp} |
| **Statistik** | Erwartungswert, Varianz, Standardabweichung, Quantil | {ref:kap:unsicherheit}, 1114 |
| **Python** | Listen, Dictionaries, Schleifen, Funktionen, Klassen, NumPy-Arrays | durchgängig |
@ -132,8 +132,8 @@ Eine Besonderheit dabei — die Import-Reihenfolge ist bewusst gewählt: `highsp
native Bibliothek zuerst. Der Grund: `ortools` und `highspy` bringen jeweils eine eigene
HiGHS-Kopie mit, die sich nicht im selben Python-Prozess verträgt — und `cvxpy` importiert
ein installiertes `highspy` bei der Solver-Erkennung selbst mit. Lädt deren HiGHS-Kopie
zuerst, bricht der Test beim CP-SAT-Funktionstest mit einem kryptischen
`ImportError: undefined symbol` ab; so gewinnt die Kopie von `ortools`, und CVXPY verzichtet
zuerst, bricht der Test beim CP-SAT{idx:CP-SAT}-Funktionstest mit einem kryptischen
`ImportError: undefined symbol` ab; so gewinnt die Kopie von `ortools`, und CVXPY{idx:CVXPY} verzichtet
lediglich auf sein HIGHS-Interface (für alle Buchprogramme folgenlos). Details und
Abhilfen: {ref:sec:oekosystem-ein-system-vier-programmieransaetze} und
{ref:anhang:fehlerdiagnose}.
@ -367,7 +367,7 @@ Suchen Sie sich einen aus. Sie können auch alle vier überspringen; nötig sind
* **Für die Sitzung gibt es einen Bericht, keine Tabelle.**
{ref:sec:praxisfallen-attribution} beziffert je Nebenbedingung, was sie den Plan kostet,
und formuliert daraus Sätze in Alltagssprache. Für die Frage des Betriebsrats — *„warum
ausgerechnet ich?“* — liefert {ref:kap:praxisfallen} den Constraint-Trace, der die
ausgerechnet ich?“* — liefert {ref:kap:praxisfallen} den Constraint-Trace{idx:Constraint-Trace}, der die
einzelne Zuweisung begründet.
**Ihr Weg.** Zuerst Pfad E (24 Stunden): {ref:sec:einfuehrung-historischer-kontext-und-evolution}
@ -394,14 +394,14 @@ deshalb jedes Mal, worauf eine Zahl beruht.
* **Der Engpass lässt sich benennen und bepreisen.**
{ref:sec:praxisfallen-attribution} rechnet für jede Kapazität aus, was die nächste Stunde
wert ist — und wie weit dieser Wert trägt. Im Beispiel gilt der Schattenpreis der
wert ist — und wie weit dieser Wert trägt. Im Beispiel gilt der Schattenpreis{idx:Schattenpreis} der
Lackiererei exakt bis **+15 Stunden**; eine ganze Sonderschicht von 30 Stunden bringt
deshalb 60 € statt der rechnerischen 120 €. **Die zweite Hälfte wäre bezahlt und
wirkungslos.**
* **Rüstzeiten, Reihenfolgen, Taktzeiten sind der Kern, nicht die Ausnahme.**
{ref:kap:cpsat} modelliert Maschinenbelegung mit Intervallvariablen,
{ref:kap:metaheuristiken} zeigt am selben Fall das Gesamtbild: Faustregel 2 497 Minuten
Rüstzeit, CP-SAT allein 2 628, Simulated Annealing 2 343, Annealing plus LNS **2 289**
Rüstzeit{idx:Rüstzeit}, CP-SAT allein 2 628, Simulated Annealing{idx:Simulated Annealing} 2 343, Annealing plus LNS **2 289**
und die untere Schranke bei 1 768, damit sichtbar bleibt, wie viel Luft noch da ist.
* **Wenn der Solver zu lange braucht, gibt es Zahlen statt Ratschläge.**
{ref:sec:cpsat-parallele-suche} misst, was mehrere Suchstränge bringen (hier Faktor 12,3)
@ -422,23 +422,23 @@ nachverhandelt, gewinnt nichts und hält das Verfahren für nutzlos.
> Titel, null in fünf anderen. Und im nächsten Quartal sieht sie völlig anders aus.“*
* **Der Grund dafür steht in {ref:kap:finanzdaten}, nicht in {ref:kap:markowitz}.** Eine
aus kurzen Zeitreihen geschätzte Kovarianzmatrix ist schlecht konditioniert; der
aus kurzen Zeitreihen geschätzte Kovarianzmatrix{idx:Kovarianzmatrix} ist schlecht konditioniert; der
Optimierer setzt dann auf Schätzrauschen. Shrinkage nach Ledoit-Wolf ist das Gegenmittel;
ihre Wirkung wird an **Eigenwertspektrum und Konditionszahl** gemessen, nicht behauptet.
ihre Wirkung wird an **Eigenwertspektrum und Konditionszahl**{idx:Konditionszahl} gemessen, nicht behauptet.
* **Der VaR verschweigt genau das, worauf es ankommt.** In der Stichprobe aus
{ref:kap:cvar} sagt der VaR: „an 95 % der Tage höchstens **1,86 %** Verlust“. Der CVaR
sagt: „und wenn doch, dann im Mittel **2,99 %**“. Der schlechteste Tag der Stichprobe
liegt bei **23,0 %** — am VaR ändert dieser eine Tag **nichts**, am CVaR sehr wohl.
Deshalb ist der CVaR die Größe, die man optimiert.
* **Transaktionskosten und Rebalancing sind Teil des Modells, nicht ein Abzug danach.**
{ref:kap:cvar} nimmt sie in die Zielfunktion, {ref:kap:handelsmaschine} setzt den
* **Transaktionskosten{idx:Transaktionskosten} und Rebalancing sind Teil des Modells, nicht ein Abzug danach.**
{ref:kap:cvar} nimmt sie in die Zielfunktion{idx:Zielfunktion}, {ref:kap:handelsmaschine} setzt den
Backtest daneben — samt der Fallen, die einen guten Backtest wertlos machen.
**Ihr Weg.** Pfad D, und zwar **mit** {ref:kap:finanzdaten}. Danach Projekt **P6**
(Portfolio-Rebalancer) oder **P7** (Risikoreport mit CVaR und Stresstests).
**Woran es scheitert.** An der Reihenfolge. Wer bei Markowitz einsteigt und die
Datenkapitel überspringt, bekommt ein Modell, das seine eigenen Schätzfehler maximiert —
Datenkapitel überspringt, bekommt ein Modell, das seine eigenen Schätzfehler{idx:Schätzfehler} maximiert —
und merkt es erst im Echtgeldbetrieb.
### Für Studierende und Data Scientists — die Methodik {-}
@ -540,43 +540,43 @@ Pfad E oder um gezielt nach einem Thema zu suchen:
| --- | --- | --- |
| `Installationstest.py` | Installationsprüfung | Vorspann |
| `Bot_Allokation.py` | Erstes CP-SAT-Modell | {ref:kap:einfuehrung} |
| `Brute_Force_Vergleich.py` | Kombinatorische Explosion | {ref:kap:einfuehrung} |
| `Brute_Force_Vergleich.py` | Kombinatorische Explosion{idx:Kombinatorische Explosion} | {ref:kap:einfuehrung} |
| `Bausteine_Vorlage.py` | Vorlage für eigene Modelle | {ref:kap:einfuehrung} |
| `Excel_Bruecke.py` | Excel-Mappe lesen, lösen, zurückschreiben | {ref:kap:einfuehrung} |
| `Matrixform.py` | Matrixform und Zulässigkeit | {ref:kap:fundament} |
| `Matrixform.py` | Matrixform{idx:Matrixform} und Zulässigkeit | {ref:kap:fundament} |
| `Konvexitaet_Demo.py` | Sehnen-Test, lokale Optima | {ref:kap:fundament} |
| `Visualisierung_Loesungsraum.py` | Polyeder mit Ecken | {ref:kap:fundament} |
| `Visualisierung_Loesungsraum.py` | Polyeder{idx:Polyeder} mit Ecken | {ref:kap:fundament} |
| `Skalierung_Kondition.py` | Konditionszahl, Ruiz, Toleranzen | {ref:kap:fundament} |
| `Solver_Wahl.py` | Entscheidungshilfe | {ref:kap:oekosystem} |
| `Ein_System_Vier_Ansaetze.py` | Vier Bibliotheken | {ref:kap:oekosystem} |
| `Modellierungsschichten.py` | Pyomo und Linopy | {ref:kap:oekosystem} |
| `Modellierungsschichten.py` | Pyomo{idx:Pyomo} und Linopy{idx:Linopy} | {ref:kap:oekosystem} |
| `Vektorisierte_Modellgenerierung.py` | Aufbauzeit vs. Lösezeit | {ref:kap:oekosystem} |
| `Vom_Wunsch_zum_Modell.py` | Fünf Modelle auf denselben Daten | {ref:kap:modellierung} |
| `Simplex_Tableau_LP.py` | Simplex von Grund auf | {ref:kap:lp} |
| `Sensitivitaetsanalyse.py` | Schattenpreise | {ref:kap:lp} |
| `Dualitaet_Nachweis.py` | Primal-dual, starke Dualität | {ref:kap:lp} |
| `Toleranzen_und_Entartung.py` | Entartung, Toleranzen, Dualspannen | {ref:kap:lp} |
| `Toleranzen_und_Entartung.py` | Entartung{idx:Entartung}, Toleranzen, Dualspannen | {ref:kap:lp} |
| `Runden_Gegenbeispiel.py` | Warum Runden scheitert | {ref:kap:milp} |
| `Rucksack.py` | Knapsack, LP-Schranke | {ref:kap:milp} |
| `MILP_Portfolio_Fixgebuehren.py` | Fixkosten, Kardinalität | {ref:kap:milp} |
| `Solverstatus_und_Gap.py` | MIP-Gap, Zeitlimit, Statusfälle | {ref:kap:milp} |
| `Solverstatus_und_Gap.py` | MIP-Gap{idx:MIP-Gap}, Zeitlimit, Statusfälle | {ref:kap:milp} |
| `Warmstart_Effekt.py` | LPT-Hinweis für CP-SAT | {ref:kap:milp} |
| `Big_M_Falle.py` | Trickle Flow bei zu großem M | {ref:kap:milp} |
| `Big_M_Falle.py` | Trickle Flow{idx:Trickle Flow} bei zu großem M | {ref:kap:milp} |
| `Propagation_Demo.py` | Propagation messbar | {ref:kap:cpsat} |
| `CP_SAT_Vertretungssystem.py` | Vertretungsplan | {ref:kap:cpsat} |
| `JobShop_Intervalle.py` | Job-Shop-Scheduling | {ref:kap:cpsat} |
| `JobShop_Intervalle.py` | Job-Shop-Scheduling{idx:Job-Shop-Scheduling} | {ref:kap:cpsat} |
| `Parallele_Suche.py` | num_workers: Tempo gegen Reproduzierbarkeit | {ref:kap:cpsat} |
| `CP_SAT_Statusfaelle.py` | Die fünf Solver-Antworten | {ref:kap:cpsat} |
| `Strafgewichte.py` | Gewichte als Wechselkurse | {ref:kap:cpsat} |
| `Min_Cost_Flow.py` | Netzwerkfluss | {ref:kap:graphen} |
| `Zuordnung_Ungarisch.py` | Zuordnung, Unimodularität | {ref:kap:graphen} |
| `VRP_Flotten_Routing.py` | CVRPTW | {ref:kap:graphen} |
| `VRP_Flotten_Routing.py` | CVRPTW{idx:CVRPTW} | {ref:kap:graphen} |
| `VRP_Kapazitaetsfalle.py` | Vergessene Dimension im Routing | {ref:kap:graphen} |
| `Simulated_Annealing.py` | Lokale Suche, Temperatur kalibrieren | {ref:kap:metaheuristiken} |
| `Metaheuristik_vs_Exakt.py` | Der Umschlagpunkt, und die Schranke | {ref:kap:metaheuristiken} |
| `Large_Neighborhood_Search.py` | Zerstören und exakt reparieren | {ref:kap:metaheuristiken} |
| `Spaltengenerierung.py` | Muster statt Stücke, Master und Pricing | {ref:kap:dekomposition} |
| `QP_Grundlagen.py` | Konvexität, DCP-Check | {ref:kap:qp-nlp} |
| `QP_Grundlagen.py` | Konvexität{idx:Konvexität}, DCP-Check | {ref:kap:qp-nlp} |
| `KKT_Nachweis.py` | KKT numerisch prüfen | {ref:kap:qp-nlp} |
| `Entropie_Maximierte_Allokation.py` | NLP mit Entropie | {ref:kap:qp-nlp} |
| `Lokale_Optima_Multistart.py` | Lokale Optima, Multistart | {ref:kap:qp-nlp} |
@ -585,8 +585,8 @@ Pfad E oder um gezielt nach einem Thema zu suchen:
| `Stochastische_Optimierung.py` | Two-Stage mit Recourse | {ref:kap:unsicherheit} |
| `Robuste_Optimierung.py` | Worst-Case-Absicherung | {ref:kap:unsicherheit} |
| `Chance_Constraints.py` | Zusage „mit 95 % Sicherheit“, SOC und Big-M | {ref:kap:unsicherheit} |
| `Bellman_Minimalbeispiel.py` | Rückwärtsinduktion | {ref:kap:dynamische-programmierung} |
| `Mehrziel_Pareto.py` | Pareto-Front, ε-Constraint, Gewichtslücke | {ref:kap:mehrziel} |
| `Bellman_Minimalbeispiel.py` | Rückwärtsinduktion{idx:Rückwärtsinduktion} | {ref:kap:dynamische-programmierung} |
| `Mehrziel_Pareto.py` | Pareto-Front{idx:Pareto-Front}, ε-Constraint, Gewichtslücke | {ref:kap:mehrziel} |
| `Predict_then_Optimize.py` | MSE gegen Entscheidungskosten | {ref:kap:prognose} |
| `Strukturbruecke.py` | Derselbe Code über Werkstatt und Depot | {ref:kap:bruecke} |
| `Kraftwerkseinsatz.py` | Unit Commitment unter Windunsicherheit | {ref:kap:supplychain} |
@ -598,9 +598,9 @@ Pfad E oder um gezielt nach einem Thema zu suchen:
| `Markowitz_CVXPY.py` | GMV, Max Sharpe, Frontier | {ref:kap:markowitz} |
| `Diversifikation_Demo.py` | Korrelation und Portfoliorisiko | {ref:kap:markowitz} |
| `Renditeschaetzung_Falle.py` | Schätzfehler in erwarteten Renditen | {ref:kap:markowitz} |
| `VaR_CVaR_Demo.py` | Fat Tails, Subadditivität | {ref:kap:cvar} |
| `VaR_CVaR_Demo.py` | Fat Tails{idx:Fat Tails}, Subadditivität{idx:Subadditivität} | {ref:kap:cvar} |
| `CVaR_Portfolio.py` | CVaR mit Reibung | {ref:kap:cvar} |
| `QuantitativeTradingEngine.py` | Walk-Forward-Backtest | {ref:kap:handelsmaschine} |
| `QuantitativeTradingEngine.py` | Walk-Forward-Backtest{idx:Walk-Forward-Backtest} | {ref:kap:handelsmaschine} |
| `Backtest_Fallen.py` | Fünf Selbsttäuschungen | {ref:kap:handelsmaschine} |
| `Data_Snooping.py` | Bestes aus N Versuchen auf Rauschen | {ref:kap:handelsmaschine} |
| `Infeasibility_Diagnose.py` | Notfallplan statt Fehler | {ref:kap:praxisfallen} |
@ -613,7 +613,7 @@ Pfad E oder um gezielt nach einem Thema zu suchen:
| `Mutationstest.py` | Findet die Lücken der eigenen Tests | {ref:kap:testing} |
| `Benchmark_Skalierung.py` | Aufbau gegen Lösen, vier Bibliotheken | {ref:kap:testing} |
| `Optimierungsdienst.py` | Das Modell als HTTP-Dienst | {ref:kap:testing} |
| `Konfliktsuche.py` | Deletion Filter: den Widerspruch einkreisen | {ref:anhang:fehlerdiagnose} |
| `Konfliktsuche.py` | Deletion Filter{idx:Deletion Filter}: den Widerspruch einkreisen | {ref:anhang:fehlerdiagnose} |
---
@ -621,7 +621,7 @@ Pfad E oder um gezielt nach einem Thema zu suchen:
Es ist **keine Anleitung zum Bau eigener Solver** — wir nutzen ausgereifte Implementierungen
und investieren die Zeit stattdessen in die Modellierung. Die einzige Ausnahme ist der
Simplex-Algorithmus in {ref:kap:lp}, den wir von Grund auf programmieren, weil man ihn
Simplex-Algorithmus{idx:Simplex-Algorithmus} in {ref:kap:lp}, den wir von Grund auf programmieren, weil man ihn
verstanden haben muss, um Dualität und Schattenpreise zu begreifen.
Es ist **kein Buch über maschinelles Lernen**. Prognosen kommen nur so weit vor, wie sie als

View file

@ -11,21 +11,21 @@ sobald Ihnen ein Symbol oder ein Kürzel begegnet, das Sie nicht sicher zuordnen
| Symbol | Sprechweise | Bedeutung | Beispiel aus dem Buch |
| --- | --- | --- | --- |
| $x$ | „iks“ | **Entscheidungsvariable(n)** — das, was der Solver festlegen darf | Anzahl gestarteter Trading-Bots ({ref:kap:einfuehrung}) |
| $x$ | „iks“ | **Entscheidungsvariable{idx:Entscheidungsvariable}(n)** — das, was der Solver festlegen darf | Anzahl gestarteter Trading-Bots ({ref:kap:einfuehrung}) |
| $\mathbf{x}$ | „Vektor iks“ | Alle Entscheidungsvariablen als Spaltenvektor | $\mathbf{x} = (x_1, \dots, x_n)^\top$ |
| $n$ | „en“ | Anzahl der Entscheidungsvariablen | 10 Aktien $\Rightarrow n = 10$ |
| $m$ | „em“ | Anzahl der Nebenbedingungen | 3 Ressourcen $\Rightarrow m = 3$ |
| $\mathbf{c}$ | „Vektor ze“ | **Kosten- bzw. Ertragsvektor** der Zielfunktion | Gewinn je Bot-Typ: $(150, 250)^\top$ |
| $\mathbf{A}$ | „Matrix a“ | **Technologiematrix** — wie viel Ressource verbraucht welche Variable | Zeile „RAM“: $(4, 6)$ |
| $\mathbf{b}$ | „Vektor be“ | **Kapazitätsvektor**, „rechte Seite“ der Ungleichungen | $(40, 60)^\top$ vCPU und GB |
| $\mathbf{c}$ | „Vektor ze“ | **Kosten- bzw. Ertragsvektor** der Zielfunktion{idx:Zielfunktion} | Gewinn je Bot-Typ: $(150, 250)^\top$ |
| $\mathbf{A}$ | „Matrix a“ | **Technologiematrix**{idx:Technologiematrix} — wie viel Ressource verbraucht welche Variable | Zeile „RAM“: $(4, 6)$ |
| $\mathbf{b}$ | „Vektor be“ | **Kapazitätsvektor**{idx:Kapazitätsvektor}, „rechte Seite“ der Ungleichungen | $(40, 60)^\top$ vCPU und GB |
| $f(\mathbf{x})$ | „ef von iks“ | **Zielfunktion** — die eine Zahl, die bewertet wird | Tagesgewinn in Euro |
| $g_i(\mathbf{x})$ | „ge i von iks“ | $i$-te **Ungleichungs-Nebenbedingung**, Form $g_i(\mathbf{x}) \le 0$ | „RAM-Verbrauch minus 60 GB“ |
| $h_j(\mathbf{x})$ | „ha j von iks“ | $j$-te **Gleichungs-Nebenbedingung**, Form $h_j(\mathbf{x}) = 0$ | „Summe der Gewichte minus 1“ |
| $s_i$ | „es i“ | **Schlupfvariable** — ungenutzte Reserve der Ressource $i$ | 1 GB RAM übrig $\Rightarrow s_2 = 1$ |
| $y_i,\ \lambda_i$ | „y i“, „lambda i“ | **Dualvariable / Schattenpreis** der Bedingung $i$ | 33,33 € je zusätzlicher Prüfstunde |
| $s_i$ | „es i“ | **Schlupfvariable**{idx:Schlupfvariable} — ungenutzte Reserve der Ressource $i$ | 1 GB RAM übrig $\Rightarrow s_2 = 1$ |
| $y_i,\ \lambda_i$ | „y i“, „lambda i“ | **Dualvariable / Schattenpreis**{idx:Schattenpreis} der Bedingung $i$ | 33,33 € je zusätzlicher Prüfstunde |
| $M$ | „groß em“ | **Big-M** — hinreichend große Konstante zum Ein-/Ausschalten von Bedingungen | $x_j \le M \cdot y_j$ |
| $z_i,\ y_i$ | | **Binärvariable** $\in \{0,1\}$ — Ja/Nein-Schalter | „Position $i$ wird eröffnet“ |
| $\mathcal{F}$ | „kalligrafisches ef“ | **Zulässiger Bereich** — Menge aller erlaubten Lösungen | das Polyeder aus {ref:kap:fundament} |
| $z_i,\ y_i$ | | **Binärvariable**{idx:Binärvariable} $\in \{0,1\}$ — Ja/Nein-Schalter | „Position $i$ wird eröffnet“ |
| $\mathcal{F}$ | „kalligrafisches ef“ | **Zulässiger Bereich** — Menge{idx:Konvexität!Menge} aller erlaubten Lösungen | das Polyeder{idx:Polyeder} aus {ref:kap:fundament} |
| $Z,\ Z^*$ | „zet“, „zet Stern“ | Zielfunktionswert bzw. **optimaler** Zielfunktionswert | $Z^* = 2300$ € |
| $\mathbf{x}^*$ | „iks Stern“ | Die **optimale Lösung**. Der Stern markiert immer „im Optimum“ | $\mathbf{x}^* = (7, 5)^\top$ |
@ -35,7 +35,7 @@ sobald Ihnen ein Symbol oder ein Kürzel begegnet, das Sie nicht sicher zuordnen
| --- | --- | --- |
| $\mathbb{R}$ | Reelle Zahlen (beliebig teilbar) | Variable darf 3,7 sein — **LP**, schnell lösbar |
| $\mathbb{R}^n$ | $n$-Tupel reeller Zahlen | Der Raum, in dem $\mathbf{x}$ lebt |
| $\mathbb{Z}$ | Ganze Zahlen | Variable muss 3 oder 4 sein — **MILP**, NP-schwer |
| $\mathbb{Z}$ | Ganze Zahlen | Variable muss 3 oder 4 sein — **MILP**, NP-schwer{idx:NP-schwer} |
| $\mathbb{N}_0$ | Natürliche Zahlen einschließlich 0 | Stückzahlen, nie negativ |
| $\{0, 1\}$ | Nur zwei Werte | Ja/Nein-Entscheidung |
| $\in$ | „ist Element von“ | $x \in \mathbb{Z}$: „$x$ ist ganzzahlig“ |
@ -49,16 +49,16 @@ sobald Ihnen ein Symbol oder ein Kürzel begegnet, das Sie nicht sicher zuordnen
| Symbol | Bedeutung | Lesehilfe |
| --- | --- | --- |
| $\sum_{i=1}^n a_i$ | Summe | „addiere $a_1$ bis $a_n$“ |
| $\mathbf{c}^\top \mathbf{x}$ | Skalarprodukt | „$c_1 x_1 + c_2 x_2 + \dots$“ — eine einzige Zahl |
| $\mathbf{c}^\top \mathbf{x}$ | Skalarprodukt{idx:Skalarprodukt} | „$c_1 x_1 + c_2 x_2 + \dots$“ — eine einzige Zahl |
| $^\top$ | Transponiert | dreht Zeilen und Spalten; macht aus Spalten- einen Zeilenvektor |
| $\mathbf{A}\mathbf{x}$ | Matrix-Vektor-Produkt | „berechne alle Ressourcenverbräuche auf einmal“ |
| $\mathbf{A}\mathbf{x}$ | Matrix-Vektor-Produkt{idx:Matrix-Vektor-Produkt} | „berechne alle Ressourcenverbräuche auf einmal“ |
| $\min_x,\ \max_x$ | Minimum/Maximum über $x$ | „wähle $x$ so, dass … kleinst-/größtmöglich wird“ |
| $\arg\min$ | Argument des Minimums | nicht der *Wert*, sondern die *Stelle* des Minimums |
| $\nabla f$ | „Nabla ef“, Gradient | Vektor aller partiellen Ableitungen — zeigt bergauf |
| $\nabla f$ | „Nabla ef“, Gradient{idx:Gradient} | Vektor aller partiellen Ableitungen — zeigt bergauf |
| $\partial Z / \partial b_i$ | Partielle Ableitung | „wie ändert sich $Z$, wenn nur $b_i$ sich ändert?“ |
| $\Vert \mathbf{v} \Vert_1$ | $L_1$-Norm | Summe der Beträge: $\vert v_1\vert + \vert v_2\vert + \dots$ |
| $\Vert \mathbf{v} \Vert_2$ | $L_2$-Norm (euklidisch) | die gewöhnliche Länge eines Pfeils |
| $\mathbf{P} \succeq 0$ | positiv **semi**definit | alle Eigenwerte $\ge 0$; Funktion ist konvex |
| $\mathbf{P} \succeq 0$ | positiv **semi**definit | alle Eigenwerte $\ge 0$; Funktion{idx:Konvexität!Funktion} ist konvex |
| $\mathbf{P} \succ 0$ | positiv **definit** | alle Eigenwerte $> 0$; Funktion ist *streng* konvex |
| $\mathbb{E}[\cdot]$ | Erwartungswert | gewichteter Mittelwert über alle Szenarien |
| $\mathbb{1}$ oder $\mathbf{1}$ | Einsvektor | $(1,1,\dots,1)^\top$; $\mathbf{1}^\top\mathbf{w}$ ist die Summe aller $w_i$ |
@ -72,15 +72,15 @@ sobald Ihnen ein Symbol oder ein Kürzel begegnet, das Sie nicht sicher zuordnen
| $\mathbf{w}$ | **Portfoliogewichte** — Kapitalanteile je Titel | dimensionslos, Summe $= 1$ |
| $w_i$ | Anteil von Titel $i$ | $0{,}15 \equiv 15\,\%$ |
| $\boldsymbol{\mu}$ | „mü“ — Vektor der **erwarteten Renditen** | z. B. $0{,}11$ = 11 % p. a. |
| $\boldsymbol{\Sigma}$ | „Sigma“ — **Kovarianzmatrix** der Renditen | $N \times N$, Einheit Rendite² |
| $\sigma$ | „sigma“ — **Volatilität** (Standardabweichung) | $0{,}20$ = 20 % p. a. |
| $\boldsymbol{\Sigma}$ | „Sigma“ — **Kovarianzmatrix**{idx:Kovarianzmatrix} der Renditen | $N \times N$, Einheit Rendite² |
| $\sigma$ | „sigma“ — **Volatilität**{idx:Volatilität} (Standardabweichung) | $0{,}20$ = 20 % p. a. |
| $\sigma^2$ | **Varianz** | Quadrat der Volatilität |
| $\mathbf{S}$ | **Stichproben**-Kovarianzmatrix (aus Daten geschätzt) | verrauscht |
| $\mathbf{F}$ | **Shrinkage-Target** — strukturierte Vergleichsmatrix | verzerrt, aber stabil |
| $\delta$ | „delta“ — Shrinkage-Intensität | $\in [0,1]$, z. B. $0{,}18$ |
| $r_f$ | *risk-free rate*, **risikoloser Zins** | z. B. $0{,}03$ = 3 % p. a. |
| $R_{i,t}$ | **Diskrete Rendite** von Titel $i$ am Tag $t$ | $P_t/P_{t-1} - 1$ |
| $r_{i,t}$ | **Logarithmische Rendite** (stetige Rendite) | $\ln(P_t/P_{t-1})$ |
| $R_{i,t}$ | **Diskrete Rendite**{idx:Diskrete Rendite} von Titel $i$ am Tag $t$ | $P_t/P_{t-1} - 1$ |
| $r_{i,t}$ | **Logarithmische Rendite**{idx:Logarithmische Rendite} (stetige Rendite) | $\ln(P_t/P_{t-1})$ |
| $P_{i,t}$ | **Kurs** (bereinigter Schlusskurs) von Titel $i$ | in Währungseinheiten |
| $\lambda$ | Risikoaversion / Gewichtung des Risikoterms | Modellparameter, frei wählbar |
| $\alpha$ | Konfidenzniveau bei VaR/CVaR | $0{,}95$ = „schlechteste 5 % der Tage“ |
@ -100,7 +100,7 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| --- | --- | --- | --- |
| $\pi$ | „pi“ | **Reihenfolge (Permutation)**: $\pi(k)$ ist der Auftrag, der als $k$-ter läuft | {ref:kap:metaheuristiken} |
| $\Delta$ | „delta“ | **Zugbewertung** — um wie viel ein Nachbarschaftszug die Lösung verschlechtert ($\Delta > 0$) oder verbessert ($\Delta \le 0$) | {ref:kap:metaheuristiken} |
| $T$ | „te“ | **Temperatur** im Simulated Annealing — steuert, wie oft eine Verschlechterung angenommen wird | {ref:kap:metaheuristiken} |
| $T$ | „te“ | **Temperatur** im Simulated Annealing{idx:Simulated Annealing} — steuert, wie oft eine Verschlechterung angenommen wird | {ref:kap:metaheuristiken} |
| $e^{-\Delta/T}$ | | **Annahmewahrscheinlichkeit** (Metropolis-Kriterium): kleine Verschlechterungen fast immer, große fast nie | {ref:kap:metaheuristiken} |
| $\pi_i$ | „pi i“ | **Dualpreis** der Bedingung $i$ im Master-LP — der Preis, mit dem das Pricing rechnet | {ref:kap:dekomposition} |
| $a_i$ | „a i“ | Wie oft Stück $i$ in einem **Schnittmuster** vorkommt (eine Spalte des Masters) | {ref:kap:dekomposition} |
@ -115,7 +115,7 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
> **⚠️ Achtung, Doppelbelegungen.** In der Literatur — und deshalb auch hier — tragen einige
> Buchstaben je nach Kapitel verschiedene Bedeutungen. Die wichtigsten Fälle:
> $\lambda$ ist in {ref:kap:lp} und {ref:kap:qp-nlp} ein **Lagrange-Multiplikator/Schattenpreis**, in {ref:kap:markowitz}
> $\lambda$ ist in {ref:kap:lp} und {ref:kap:qp-nlp} ein **Lagrange-Multiplikator{idx:Lagrange-Multiplikator}/Schattenpreis**, in {ref:kap:markowitz}
> und {ref:kap:cvar} ein **frei gewählter Risikoaversionsparameter**. $\gamma$ ist in {ref:kap:dynamische-programmierung} der
> **Diskontfaktor**, in {ref:kap:cvar} die **VaR-Hilfsvariable**. $S$ bezeichnet als Matrix
> $\mathbf{S}$ die Stichprobenkovarianz, als Skalar $S$ die Szenarienzahl. $\pi$ ist in
@ -140,20 +140,20 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| **QP** | Quadratic Program(ming) | Quadratische Optimierung | {ref:kap:qp-nlp} |
| **MIQP** | Mixed-Integer Quadratic Program(ming) | Ganzzahlig-quadratische Optimierung | {ref:kap:markowitz} |
| **NLP** | Nonlinear Program(ming) | Nichtlineare Optimierung | {ref:kap:qp-nlp} |
| **MINLP** | Mixed-Integer Nonlinear Program(ming) | Ganzzahlig-nichtlineare Optimierung | {ref:kap:oekosystem} |
| **MINLP**{idx:MINLP} | Mixed-Integer Nonlinear Program(ming) | Ganzzahlig-nichtlineare Optimierung | {ref:kap:oekosystem} |
| **CP** | Constraint Programming | Bedingungsprogrammierung | {ref:kap:cpsat} |
| **SAT** | Boolean **Sat**isfiability Problem | Erfüllbarkeitsproblem der Aussagenlogik | {ref:kap:cpsat} |
| **CP-SAT** | Constraint Programming über SAT-Techniken | Solver-Name von Google OR-Tools | {ref:kap:cpsat} |
| **CDCL** | Conflict-Driven Clause Learning | konfliktgetriebenes Klausellernen | {ref:kap:cpsat} |
| **CP-SAT**{idx:CP-SAT} | Constraint Programming über SAT-Techniken | Solver-Name von Google OR-Tools{idx:OR-Tools} | {ref:kap:cpsat} |
| **CDCL**{idx:CDCL} | Conflict-Driven Clause Learning | konfliktgetriebenes Klausellernen | {ref:kap:cpsat} |
| **DP** | Dynamic Programming | Dynamische Programmierung | {ref:kap:dynamische-programmierung} |
| **B&B** | Branch and Bound | Verzweigen und Beschränken | {ref:kap:milp} |
| **B&C** | Branch and Cut | Verzweigen mit Schnittebenen | {ref:kap:milp} |
| **KKT** | KarushKuhnTucker (Bedingungen) | Optimalitätsbedingungen bei Restriktionen | {ref:kap:qp-nlp} |
| **SLSQP** | Sequential Least Squares Programming | sequentielle quadratische Optimierung | {ref:kap:qp-nlp} |
| **RMT** | Random Matrix Theory | Zufallsmatrizentheorie | {ref:kap:finanzdaten} |
| **SA** | Simulated Annealing | Simulierte Abkühlung — Metaheuristik, die Verschlechterungen mit fallender Wahrscheinlichkeit zulässt | {ref:kap:metaheuristiken} |
| **SLSQP**{idx:SLSQP} | Sequential Least Squares Programming | sequentielle quadratische Optimierung | {ref:kap:qp-nlp} |
| **RMT** | Random Matrix Theory | Zufallsmatrizentheorie{idx:Zufallsmatrizentheorie} | {ref:kap:finanzdaten} |
| **SA** | Simulated Annealing | Simulierte Abkühlung — Metaheuristik{idx:Metaheuristik}, die Verschlechterungen mit fallender Wahrscheinlichkeit zulässt | {ref:kap:metaheuristiken} |
| **LNS** | Large Neighborhood Search | Große Nachbarschaftssuche: Teil der Lösung verwerfen, Rest exakt neu bauen | {ref:kap:metaheuristiken} |
| **CG** | Column Generation | Spaltengenerierung — Variablen erst erzeugen, wenn sie sich lohnen | {ref:kap:dekomposition} |
| **CG** | Column Generation{idx:Column Generation} | Spaltengenerierung — Variablen erst erzeugen, wenn sie sich lohnen | {ref:kap:dekomposition} |
| **IIS** | Irreducible Infeasible Subset | Kleinste widersprüchliche Teilmenge von Bedingungen | {ref:anhang:fehlerdiagnose} |
| **MSE** | Mean Squared Error | Mittlerer quadratischer Fehler — das Standardmaß für Prognosegüte | {ref:kap:prognose} |
@ -165,9 +165,9 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| **VRP** | Vehicle Routing Problem | Tourenplanungsproblem | {ref:kap:graphen} |
| **CVRP** | **C**apacitated VRP | VRP mit Fahrzeugkapazitäten | {ref:kap:graphen} |
| **VRPTW** | VRP with **T**ime **W**indows | VRP mit Zeitfenstern | {ref:kap:graphen} |
| **CVRPTW** | Capacitated VRP with Time Windows | mit Kapazitäten *und* Zeitfenstern | {ref:kap:graphen} |
| **CVRPTW**{idx:CVRPTW} | Capacitated VRP with Time Windows | mit Kapazitäten *und* Zeitfenstern | {ref:kap:graphen} |
| **MCNFP** | Minimum-Cost Network Flow Problem | Kostenminimales Flussproblem | {ref:kap:graphen} |
| **MTZ** | MillerTuckerZemlin (Formulierung) | Kurzzyklus-Eliminierung | {ref:kap:graphen} |
| **MTZ** | MillerTuckerZemlin (Formulierung) | Kurzzyklus-Eliminierung{idx:Subtour!Eliminierung} | {ref:kap:graphen} |
| **JSSP** | Job-Shop Scheduling Problem | Werkstattfertigungs-Reihenfolgeproblem | {ref:kap:cpsat} |
### 2.3 Finanz- und Risikokennzahlen
@ -177,12 +177,12 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| **MPT** | Modern Portfolio Theory | Moderne Portfoliotheorie (Markowitz) | {ref:kap:markowitz} |
| **GMV** | Global Minimum Variance (Portfolio) | Portfolio kleinstmöglicher Varianz | {ref:kap:markowitz} |
| **MVO** | Mean-Variance Optimization | Erwartungswert-Varianz-Optimierung | {ref:kap:markowitz} |
| **SR** | Sharpe Ratio | Überrendite je Einheit Volatilität | {ref:kap:markowitz} |
| **SR** | Sharpe Ratio{idx:Sharpe-Ratio} | Überrendite je Einheit Volatilität | {ref:kap:markowitz} |
| **VaR** | Value at Risk | Verlustschwelle bei gegebener Wahrscheinlichkeit | {ref:kap:cvar} |
| **CVaR** | Conditional Value at Risk | mittlerer Verlust jenseits des VaR | {ref:kap:cvar} |
| **ES** | Expected Shortfall | anderes Wort für CVaR | {ref:kap:cvar} |
| **ES** | Expected Shortfall{idx:Expected Shortfall} | anderes Wort für CVaR | {ref:kap:cvar} |
| **CAGR** | Compound Annual Growth Rate | durchschnittliche jährliche Wachstumsrate | {ref:kap:handelsmaschine} |
| **MDD** | Maximum Drawdown | größter Rückgang vom Höchststand | {ref:kap:handelsmaschine} |
| **MDD** | Maximum Drawdown{idx:Maximum Drawdown} | größter Rückgang vom Höchststand | {ref:kap:handelsmaschine} |
| **LW** | LedoitWolf (Shrinkage) | Schrumpfungsverfahren für Kovarianzmatrizen | {ref:kap:finanzdaten} |
| **p. a.** | *per annum* | pro Jahr | {ref:teil:anwendungen} |
| **bp** | Basispunkt | 0,01 Prozentpunkt; 15 bp = 0,15 % | {ref:kap:cvar}, {ref:kap:handelsmaschine} |
@ -191,19 +191,19 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| Kürzel | Ausgeschrieben | Was es ist | Lizenz |
| --- | --- | --- | --- |
| **HiGHS** | **Hi**gh Performance **G**eneral-purpose **S**olver | LP-, MILP- und QP-Solver in C++ | Open Source (MIT) |
| **HiGHS**{idx:HiGHS} | **Hi**gh Performance **G**eneral-purpose **S**olver | LP-, MILP- und QP-Solver in C++ | Open Source (MIT) |
| **OR-Tools** | Google Operations Research Tools | Solver-Sammlung (CP-SAT, Routing, Wrapper) | Open Source (Apache 2.0) |
| **CVXPY** | **C**on**v**e**x** **Py**thon | Modellierungssprache für konvexe Optimierung | Open Source (Apache 2.0) |
| **CVXPY**{idx:CVXPY} | **C**on**v**e**x** **Py**thon | Modellierungssprache für konvexe Optimierung | Open Source (Apache 2.0) |
| **SciPy** | **Sci**entific **Py**thon | Wissenschaftliches Python-Paket; `scipy.optimize` liefert LP- (über HiGHS), Zuordnungs- (`linear_sum_assignment`) und NLP-Solver (`minimize`) | Open Source (BSD) |
| **GLOP** | **G**oogle **L**inear **Op**timizer | LP-Solver in OR-Tools | Open Source |
| **SCIP** | Solving Constraint Integer Programs | MILP/MINLP-Solver | akademisch frei |
| **SCIP**{idx:SCIP} | Solving Constraint Integer Programs | MILP/MINLP-Solver | akademisch frei |
| **CBC** | **C**oin-or **B**ranch and **C**ut | MILP-Solver des COIN-OR-Projekts | Open Source |
| **GLPK** | GNU Linear Programming Kit | LP/MILP-Solver | Open Source (GPL) |
| **OSQP** | **O**perator **S**plitting **Q**uadratic **P**rogram | QP-Solver | Open Source |
| **ECOS** | Embedded Conic Solver | konischer Solver | Open Source |
| **SCS** | Splitting Conic Solver | konischer Solver | Open Source |
| **IPOPT** | **I**nterior **Po**int **Opt**imizer | NLP-Solver | Open Source (EPL) |
| **Gurobi**, **CPLEX**, **Xpress** | — | kommerzielle Hochleistungs-Solver | kostenpflichtig |
| **Gurobi**{idx:Gurobi}, **CPLEX**, **Xpress** | — | kommerzielle Hochleistungs-Solver | kostenpflichtig |
| **API** | Application Programming Interface | Programmierschnittstelle | — |
| **DSL** | Domain-Specific Language | fachspezifische Sprache (hier: Modellierungssprache) | — |
| **CSR** | Compressed Sparse Row | Speicherformat für dünnbesetzte Matrizen | — |
@ -220,12 +220,12 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| **FEASIBLE** | Zulässige Lösung gefunden, Optimalität nicht bewiesen | Zeitlimit erhöhen oder Gap akzeptieren |
| **INFEASIBLE** | Keine Lösung erfüllt alle Bedingungen | {ref:anhang:fehlerdiagnose}: Diagnose durchführen |
| **UNBOUNDED** | Zielfunktion unbeschränkt verbesserbar | Fehlende Nebenbedingung — Modell prüfen |
| **MIP-Gap** | Relativer Abstand beste Lösung ↔ beste Schranke | 12 % sind in der Praxis meist genug |
| **Incumbent** | Beste bisher gefundene zulässige Lösung | Referenz für das Pruning |
| **Relaxation** | Modell mit weggelassener Ganzzahligkeit | liefert die Schranke |
| **MIP-Gap**{idx:MIP-Gap} | Relativer Abstand beste Lösung ↔ beste Schranke | 12 % sind in der Praxis meist genug |
| **Incumbent**{idx:Branch-and-Bound!Incumbent} | Beste bisher gefundene zulässige Lösung | Referenz für das Pruning |
| **Relaxation**{idx:Relaxation} | Modell mit weggelassener Ganzzahligkeit | liefert die Schranke |
| **Warm Start** | Solver startet von bekannter Lösung | beschleunigt wiederholte Läufe |
| **Slack** | Schlupf, ungenutzte Kapazität | Slack = 0 → Engpass |
| **Lookahead-Bias** | Nutzung von Daten aus der Zukunft | Backtest ist wertlos — {ref:kap:praxisfallen} |
| **Lookahead-Bias**{idx:Lookahead-Bias} | Nutzung von Daten aus der Zukunft | Backtest ist wertlos — {ref:kap:praxisfallen} |
---

View file

@ -10,7 +10,7 @@
>
> **Voraussetzungen:** Keine. Dies ist der Einstieg.
>
> **Danach können Sie:** Ein Alltagsproblem in Entscheidungsvariablen, Parameter, Zielfunktion und Nebenbedingungen zerlegen, ein erstes Modell mit Google OR-Tools lösen — und die Daten dafür aus einer Excel-Mappe holen und das Ergebnis dorthin zurückschreiben.
> **Danach können Sie:** Ein Alltagsproblem in Entscheidungsvariablen, Parameter{idx:Parameter (Modell)}, Zielfunktion{idx:Zielfunktion} und Nebenbedingungen zerlegen, ein erstes Modell mit Google OR-Tools{idx:OR-Tools} lösen — und die Daten dafür aus einer Excel-Mappe holen und das Ergebnis dorthin zurückschreiben.
>
> **Zeitbedarf:** ca. 3,5 Stunden inklusive Übungen.
>
@ -67,7 +67,7 @@ Block, führen Sie ihn aus, sehen Sie das Ergebnis — die Erklärung kommt dana
geantwortet? Die naheliegende Antwort lautet „möglichst viele Tische“ — der Tisch bringt
mit 240 € schließlich den vierfachen Deckungsbeitrag eines Stuhls. Rechnen wir das durch:
| Strategie | Menge | Deckungsbeitrag |
| Strategie | Menge{idx:Konvexität!Menge} | Deckungsbeitrag |
| --- | --- | ---: |
| Nur Tische (Bauchgefühl) | 40 Tische (das Plattenmaterial ist zuerst leer) | 9 600 € |
| Nur Stühle | 150 Stühle (die Montagezeit ist zuerst leer) | 9 000 € |
@ -200,7 +200,7 @@ Und die Rechnung ist noch geschönt: Ein schnellerer Rechner hilft praktisch nic
eine Beschleunigung um den Faktor **eine Million** verschiebt die Grenze von $n=20$ nur auf
etwa $n=24$. Gegen kombinatorisches Wachstum ist Hardware machtlos.
Das folgende Programm macht diesen Effekt erfahrbar. Es löst dasselbe Zuordnungsproblem
Das folgende Programm macht diesen Effekt erfahrbar. Es löst dasselbe Zuordnungsproblem{idx:Zuordnungsproblem (1:1)}
zweimal — einmal durch vollständiges Durchprobieren, einmal mit einem OR-Solver — und
vergleicht die Laufzeiten.
@ -343,7 +343,7 @@ zusätzlichen Element, die Solver-Zeit wächst kaum merklich.
> | ----------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------ |
> | `itertools.permutations(range(n))` | erzeugt nacheinander alle $n!$ Reihenfolgen | die Definition von „alles durchprobieren“ |
> | `wert = sum(kosten[i][zuordnung[i]] ...)` | bewertet eine einzelne Kombination | das ist die **Zielfunktion**, nur eben von Hand ausgewertet |
> | `modell.NewBoolVar(...)` | legt eine Ja/Nein-**Entscheidungsvariable** an | $n^2$ Variablen statt $n!$ Kombinationen |
> | `modell.NewBoolVar(...)` | legt eine Ja/Nein-**Entscheidungsvariable**{idx:Entscheidungsvariable} an | $n^2$ Variablen statt $n!$ Kombinationen |
> | `modell.AddExactlyOne(...)` | „genau eine davon ist wahr“ | die **Nebenbedingungen** |
> | `modell.Minimize(...)` | die zu minimierende Summe | die **Zielfunktion**, jetzt deklariert statt ausgerechnet |
> | `loeser.Solve(modell)` | der Solver sucht — aber nicht stur | er nutzt Struktur (Schranken, Propagation), statt abzuzählen |
@ -419,7 +419,7 @@ Lagerkapazitäten, Steuerquoten, Entfernungen, Qualifikationen.
> **Die Leitfrage:** *Was ist gegeben und wird nicht entschieden?* Parameter sind der Ort,
> an dem Datenqualität über Modellqualität entscheidet. {ref:kap:finanzdaten} zeigt am Beispiel der
> Kovarianzmatrix, wie ein schlecht geschätzter Parameter ein mathematisch perfektes Modell
> Kovarianzmatrix{idx:Kovarianzmatrix}, wie ein schlecht geschätzter Parameter ein mathematisch perfektes Modell
> wertlos macht.
### Baustein 3 — Zielfunktion ($f(x)$)
@ -862,8 +862,8 @@ Solver-Ergebnisse wo immer möglich gegen eine unabhängige Rechnung**, gerade a
> | ---------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> | Konstanten oben | alle Parameter an einer Stelle | Nie Zahlen im Modellcode verstreuen — sonst findet man sie beim Ändern nicht wieder |
> | `NewIntVar(0, 100, ...)` | ganzzahlige Variable mit weiten Grenzen | Grenzen müssen endlich sein; 100 ist bewusst unkritisch groß |
> | `modell.Add(x_a <= MAX_ARBITRAGE)` | Marktlimit als **Nebenbedingung** | Alternativ liesse sich dieses Limit auch in die Variablengrenze packen. Beides funktioniert — aber als Nebenbedingung bleibt das Modell zur mathematischen Formulierung deckungsgleich und man kann später den Schattenpreis abfragen |
> | `loeser.StatusName(status)` | echter Status statt fester Text | Ein fest codierter Text wie „OPTIMAL“ würde unabhängig vom tatsächlichen Ergebnis ausgegeben — deshalb immer den echten Solver-Status auswerten |
> | `modell.Add(x_a <= MAX_ARBITRAGE)` | Marktlimit als **Nebenbedingung** | Alternativ liesse sich dieses Limit auch in die Variablengrenze packen. Beides funktioniert — aber als Nebenbedingung bleibt das Modell zur mathematischen Formulierung deckungsgleich und man kann später den Schattenpreis{idx:Schattenpreis} abfragen |
> | `loeser.StatusName(status)` | echter Status statt fester Text | Ein fest codierter Text wie „OPTIMAL“ würde unabhängig vom tatsächlichen Ergebnis ausgegeben — deshalb immer den echten Solver-Status{idx:Solver-Status} auswerten |
> | `assert ...` | Selbstkontrolle nach dem Lösen | Kostet nichts und fängt Modellierungsfehler, die der Solver nicht bemerken kann |
> | Vergleich mit naiver Strategie | quantifiziert den Nutzen | Die Frage „Was bringt die Optimierung überhaupt?“ kommt in jedem Projekt — beantworten Sie sie ungefragt |
@ -913,7 +913,7 @@ schlägt die Brücke — in beide Richtungen.
> | „Zielzelle“ + Max/Min | `solver.Maximize(...)` | Statt einer Formel in einer Zelle ein Ausdruck im Code. |
> | „Nebenbedingungen“-Liste | `solver.Add(...)` | Eine Zeile je Regel — oder eine Schleife für 10 000 Regeln. |
> | „Ganzzahlig“ / „Binär“ | `NewIntVar(...)` / `NewBoolVar(...)` | Gleiche Bedeutung, gleiche Konsequenz für die Laufzeit. |
> | Simplex-LP / GRG / Evolutionär | GLOP / SLSQP / CP-SAT | In Excel drei Auswahlpunkte, in Python drei Bibliotheken ({ref:kap:oekosystem}). |
> | Simplex-LP / GRG / Evolutionär | GLOP / SLSQP{idx:SLSQP} / CP-SAT{idx:CP-SAT} | In Excel drei Auswahlpunkte, in Python drei Bibliotheken ({ref:kap:oekosystem}). |
> | „Sensitivitätsbericht“ | `bedingung.dual_value()` | Der Schattenpreis — {ref:kap:lp} erklärt, was er bedeutet. |
> | 200 Zeilen sind das Limit | keine praktische Grenze | Der Excel-Solver ist auf 200 veränderbare Zellen begrenzt. |
@ -1103,8 +1103,8 @@ zu und begründen Sie in einem Satz:
(d) „Welche Kunden werden voraussichtlich kündigen?“
(e) „Welchen dieser Kunden sollen wir mit unserem begrenzten Rabattbudget halten?“
**Aufgabe ⭐ — Hart oder weich?**
Entscheiden Sie für jede Bedingung eines Klinik-Dienstplans, ob sie hart oder weich
**Aufgabe ⭐ — Hart oder weich{idx:Nebenbedingung!weich}?**
Entscheiden Sie für jede Bedingung eines Klinik-Dienstplans, ob sie hart{idx:Nebenbedingung!hart} oder weich
modelliert werden sollte, und begründen Sie:
(a) Gesetzliche Ruhezeit von 11 Stunden zwischen zwei Diensten.
(b) Frau Meier möchte freitags nicht arbeiten.
@ -1269,7 +1269,7 @@ eine Zahl liefern, die betriebswirtschaftlich Unsinn ist. Genau solche Fälle ü
**Ausblick.** {ref:kap:fundament} liefert die Sprache, um Modelle mit hunderten Variablen kompakt
aufzuschreiben: Vektoren, Matrizen und die geometrische Sicht auf den zulässigen Bereich.
Dort lernen wir auch die Eigenschaft kennen, die darüber entscheidet, ob ein Problem
verlässlich lösbar ist: **Konvexität**.
verlässlich lösbar ist: **Konvexität**{idx:Konvexität}.
---

View file

@ -1,4 +1,4 @@
# Kapitel: Das mathematische Fundament — Vektoren, Matrizen, Konvexität {#kap:fundament}
# Kapitel: Das mathematische Fundament — Vektoren, Matrizen, Konvexität{idx:Konvexität} {#kap:fundament}
> **📌 Kapitel auf einen Blick**
>
@ -8,7 +8,7 @@
>
> **Voraussetzungen:** {ref:kap:einfuehrung}. Lineare Algebra wird hier von Grund auf wiederholt.
>
> **Danach können Sie:** Ein Modell in Matrixform aufschreiben, den zulässigen Bereich
> **Danach können Sie:** Ein Modell in Matrixform{idx:Matrixform} aufschreiben, den zulässigen Bereich
> geometrisch deuten, beurteilen ob ein Problem konvex ist — und einschätzen, wie viele
> Stellen Ihres Ergebnisses überhaupt belastbar sind.
>
@ -66,13 +66,13 @@ die zentrale Einsicht in Aktion — ganz ohne Solver, mit acht Zeilen NumPy.
**Das Optimum lautet $(0, 8)$ mit $Z = 40$** — und Sie haben es gefunden, ohne einen
einzigen Solver zu starten. Vier Kandidaten statt unendlich vieler Punkte: Das ist der
**Fundamentalsatz der linearen Optimierung**, und er ist der Grund, warum sich lineare
**Fundamentalsatz der linearen Optimierung**{idx:Fundamentalsatz der linearen Optimierung}, und er ist der Grund, warum sich lineare
Programme überhaupt zuverlässig lösen lassen.
**Warum funktioniert das?** Weil der zulässige Bereich ein **konvexes Polyeder** ist — ein
Vielflächner ohne Diagonalen nach innen — und weil eine lineare Zielfunktion darauf ihren
**Warum funktioniert das?** Weil der zulässige Bereich ein **konvexes Polyeder**{idx:Polyeder} ist — ein
Vielflächner ohne Diagonalen nach innen — und weil eine lineare Zielfunktion{idx:Zielfunktion} darauf ihren
größten Wert immer am Rand annimmt, genauer: in einer Ecke. Beides begründet dieses
Kapitel. Der Simplex-Algorithmus in {ref:kap:lp} tut im Kern nichts anderes als das Programm
Kapitel. Der Simplex-Algorithmus{idx:Simplex-Algorithmus} in {ref:kap:lp} tut im Kern nichts anderes als das Programm
oben, nur klüger: Er probiert nicht alle Ecken durch, sondern läuft gezielt von Ecke zu
Ecke bergauf.
@ -92,12 +92,12 @@ Nach diesem Kapitel können Sie …
2. … erklären, warum der zulässige Bereich eines linearen Programms ein **Polyeder** ist.
3. … den Fundamentalsatz der linearen Optimierung anwenden, um Kandidaten für das Optimum
zu finden, ohne alles durchzuprobieren.
4. … prüfen, ob eine Menge bzw. eine Funktion konvex ist, und begründen, warum das für die
4. … prüfen, ob eine Menge{idx:Konvexität!Menge} bzw. eine Funktion{idx:Konvexität!Funktion} konvex ist, und begründen, warum das für die
Lösbarkeit entscheidend ist.
5. … einen zweidimensionalen Lösungsraum mit `matplotlib` zeichnen und daraus die optimale
Ecke ablesen.
6. … die **Konditionszahl** $\kappa(\mathbf{A})$ berechnen, ihren Wert deuten und ein
schlecht skaliertes Modell mit Ruiz-Equilibrierung wieder rechenbar machen.
6. … die **Konditionszahl**{idx:Konditionszahl} $\kappa(\mathbf{A})$ berechnen, ihren Wert deuten und ein
schlecht skaliertes Modell mit Ruiz-Equilibrierung{idx:Ruiz-Equilibrierung} wieder rechenbar machen.
7. … erklären, warum ein Solver-Ergebnis von `0.99999998` niemals mit `int()` in eine ganze
Zahl verwandelt werden darf.
@ -129,7 +129,7 @@ $$
\mathbf{c} = \begin{pmatrix} c_1 \\ c_2 \\ \vdots \\ c_n \end{pmatrix} \in \mathbb{R}^n
$$
**Zielfunktion als Skalarprodukt:**
**Zielfunktion als Skalarprodukt{idx:Skalarprodukt}:**
$$
f(\mathbf{x}) = \mathbf{c}^\top \mathbf{x} = \sum_{j=1}^n c_j x_j
@ -355,7 +355,7 @@ erzeugt von `bilder_04/erzeuge_polyeder.py`.](bilder_04/kap_fundament_polyeder.s
### Der Fundamentalsatz der linearen Optimierung
> **Satz.** Besitzt ein lineares Optimierungsproblem eine optimale Lösung, dann liegt
> mindestens ein optimaler Punkt auf einer **Ecke (Extrempunkt)** des Polyeders
> mindestens ein optimaler Punkt auf einer **Ecke (Extrempunkt)**{idx:Ecke (Extrempunkt)} des Polyeders
> $\mathcal{F}$.
**Warum das gilt — die Anschauung:** Die Zielfunktion $\mathbf{c}^\top\mathbf{x}$ hat
@ -475,8 +475,8 @@ etwas falsch gemacht: Beide enden dort, wo es nicht mehr bergab geht.
| --- | --- | --- |
| Lineares Programm (LP) | **ja** | Globales Optimum, Sekunden bis Minuten selbst bei $10^6$ Variablen |
| Quadratisches Programm mit $\mathbf{P} \succeq 0$ | **ja** | Globales Optimum, effizient ({ref:kap:qp-nlp}, {ref:kap:markowitz}) |
| Ganzzahlige Variablen (MILP) | **nein** | Der zulässige Bereich ist ein Punktgitter, keine zusammenhängende Menge — NP-schwer ({ref:kap:milp}) |
| Quadratisches Programm mit indefinitem $\mathbf{P}$ | **nein** | Nur lokale Optima; eine ungültige Kovarianzmatrix ist ein typischer Auslöser (siehe {ref:sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize}) |
| Ganzzahlige Variablen (MILP) | **nein** | Der zulässige Bereich ist ein Punktgitter, keine zusammenhängende Menge — NP-schwer{idx:NP-schwer} ({ref:kap:milp}) |
| Quadratisches Programm mit indefinitem $\mathbf{P}$ | **nein** | Nur lokale Optima; eine ungültige Kovarianzmatrix{idx:Kovarianzmatrix} ist ein typischer Auslöser (siehe {ref:sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize}) |
| Allgemeines NLP mit beliebigen Funktionen | meist **nein** | `scipy.optimize` liefert nur ein lokales Optimum, abhängig vom Startpunkt |
### Konvexität sichtbar machen
@ -893,7 +893,7 @@ Einheit messen“. Die Lösung rechnet man mit $\mathbf{x} = \mathbf{D}_c\,\tild
zurück.
> **🎯 Merksatz**
> Jeder ernsthafte Solver skaliert intern selbst — HiGHS, Gurobi, CP-SAT alle. Verlassen Sie
> Jeder ernsthafte Solver skaliert intern selbst — HiGHS{idx:HiGHS}, Gurobi{idx:Gurobi}, CP-SAT{idx:CP-SAT} alle. Verlassen Sie
> sich trotzdem nicht darauf: Die interne Skalierung repariert die Rechnung, nicht die
> Modellierung. Wenn Ihre Koeffizienten zwölf Größenordnungen überspannen, sagt Ihnen das
> etwas über Ihr Modell, nicht über den Solver.
@ -902,7 +902,7 @@ zurück.
Das folgende Programm führt beides vor — die Fehlerverstärkung und ihre Behebung — und
schließt mit der praktischen Konsequenz, die Sie in {ref:kap:milp} wieder brauchen werden:
warum eine Binärvariable mit dem Wert `0.99999998` niemals mit `int()` gerundet werden darf.
warum eine Binärvariable{idx:Binärvariable} mit dem Wert `0.99999998` niemals mit `int()` gerundet werden darf.
```python
#!/usr/bin/env python3

View file

@ -57,8 +57,8 @@
> Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
> ```
**Sie haben soeben HiGHS benutzt** — denselben C++-Solver, der auch hinter `highspy`, hinter
CVXPY und in vielen kommerziellen Systemen steckt. `scipy.optimize.linprog` ist nur die
**Sie haben soeben HiGHS{idx:HiGHS} benutzt** — denselben C++-Solver, der auch hinter `highspy`, hinter
CVXPY{idx:CVXPY} und in vielen kommerziellen Systemen steckt. `scipy.optimize.linprog` ist nur die
dünnste denkbare Hülle darum.
Das ist die zentrale Botschaft dieses Kapitels: **Modellierungsschicht und Solver sind zwei
@ -68,13 +68,13 @@ lassen sich unabhängig voneinander tauschen — und genau davon handelt der Res
> **⚠️ Zwei Stolpersteine stecken schon in diesen sechs Zeilen**
>
> * `linprog` **minimiert immer**. Wer maximieren will, negiert die Zielfunktion — und darf
> * `linprog` **minimiert immer**. Wer maximieren will, negiert die Zielfunktion{idx:Zielfunktion} — und darf
> nicht vergessen, das Ergebnis zurückzudrehen.
> * `linprog` kennt **nur `<=`**. Ein Mindestgehalt „$\ge 20$“ wird zu „$-12x_1 - 44x_2 \le
> -20$“. Ein Vorzeichenfehler an dieser Stelle liefert eine perfekt aussehende Lösung, die
> das Gegenteil des Gewollten erfüllt.
>
> Beide Fallen verschwinden, sobald man eine Modellierungsschicht wie CVXPY oder Pyomo
> Beide Fallen verschwinden, sobald man eine Modellierungsschicht wie CVXPY oder Pyomo{idx:Pyomo}
> benutzt, in der `>=` einfach `>=` heißt. Das ist ein Hauptgrund, warum es sie gibt.
---
@ -89,8 +89,8 @@ Nach diesem Kapitel können Sie …
formulieren.
4. … Ergebnisse verschiedener Solver gegeneinander validieren (*Cross-Check*).
5. … einschätzen, wann sich der Aufwand einer Low-Level-Schnittstelle lohnt und wann nicht.
6. … Pyomo und Linopy einordnen und begründen, wann sich der Umstieg auf sie lohnt.
7. … Aufbau- und Lösezeit getrennt messen und einen Modellaufbau mit NumPy oder Polars
6. … Pyomo und Linopy{idx:Linopy} einordnen und begründen, wann sich der Umstieg auf sie lohnt.
7. … Aufbau- und Lösezeit getrennt messen und einen Modellaufbau mit NumPy oder Polars{idx:Polars}
vektorisieren, statt ihn in Python-Schleifen zu erzeugen.
---
@ -115,7 +115,7 @@ nutzt eine **zweischichtige Architektur**{idx:Zweischichtige Architektur}:
> mit fünf verschiedenen Solvern lösen.
**Warum diese Trennung nützlich ist:** Ein CVXPY{idx:CVXPY}-Modell, das heute mit dem freien Solver
Clarabel läuft, läuft morgen ohne Codeänderung mit dem kommerziellen Gurobi — man tauscht
Clarabel läuft, läuft morgen ohne Codeänderung mit dem kommerziellen Gurobi{idx:Gurobi} — man tauscht
ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein anzufangen.
---
@ -125,10 +125,10 @@ ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein a
| Bibliothek | Stärken & Problemklassen | Wann einsetzen? | Typische Backends |
| --- | --- | --- | --- |
| `scipy.optimize` | Standard-LPs, kontinuierliche nichtlineare Optimierung, Wurzelsuche | Für elementare LPs und schnelle Prototypen ohne zusätzliche schwere Abhängigkeiten | HiGHS (`highs-ds`, `highs-ipm`) |
| `ortools` (Google) | Diskrete Planung, Schichten, Vertretungspläne, Routing (VRP/TSP), kombinatorische Logik | Wenn diskrete Ja/Nein-Entscheidungen, Zeitfenster und Wenn-Dann-Regeln dominieren | CP-SAT, GLOP, Routing Engine |
| `cvxpy` | Konvexe Optimierung, quadratische Programme, Risikomodelle, Portfolios | **Erste Wahl für Finanzoptimierung**, Markowitz, CVaR, $L_1$-Transaktionskosten | Clarabel, OSQP, ECOS, SCS, HiGHS |
| `ortools` (Google) | Diskrete Planung, Schichten, Vertretungspläne, Routing (VRP/TSP), kombinatorische Logik | Wenn diskrete Ja/Nein-Entscheidungen, Zeitfenster und Wenn-Dann-Regeln dominieren | CP-SAT{idx:CP-SAT}, GLOP, Routing Engine |
| `cvxpy` | Konvexe Optimierung, quadratische Programme, Risikomodelle, Portfolios | **Erste Wahl für Finanzoptimierung**, Markowitz, CVaR, $L_1$-Transaktionskosten{idx:Transaktionskosten} | Clarabel, OSQP, ECOS, SCS, HiGHS |
| `highspy` | Direkte Solver-Steuerung für LP/MILP, maximale Kontrolle | Wenn Sie Modelle wiederholt und schnell aufbauen oder Solver-Details steuern müssen | HiGHS |
| `Pyomo` | Universelle Modellierungssprache (LP, MILP, MINLP) | Industrielle Großmodelle mit strikter Trennung von Daten und Modell | HiGHS, Gurobi, CPLEX, SCIP, IPOPT |
| `Pyomo` | Universelle Modellierungssprache (LP, MILP, MINLP{idx:MINLP}) | Industrielle Großmodelle mit strikter Trennung von Daten und Modell | HiGHS, Gurobi, CPLEX, SCIP{idx:SCIP}, IPOPT |
| `Linopy` | LP/MILP über beschriftete Arrays (`xarray`) | Modelle mit zehntausenden gleichartigen Nebenbedingungen (Energie, Netze, Zeitreihen) | HiGHS, GLPK, CBC, Gurobi |
### Die Entscheidung in drei Fragen
@ -136,7 +136,7 @@ ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein a
Statt die Tabelle auswendig zu lernen, beantworten Sie drei Fragen:
> **Frage 1: Gibt es Ja/Nein-Entscheidungen oder Reihenfolgen?**
> → **Ja:** OR-Tools (CP-SAT) bei Zuweisung/Scheduling, oder MILP über HiGHS bei
> → **Ja:** OR-Tools{idx:OR-Tools} (CP-SAT) bei Zuweisung/Scheduling, oder MILP über HiGHS bei
> ökonomischen Fixkostenmodellen.
> → **Nein:** weiter zu Frage 2.
>
@ -315,7 +315,7 @@ Zeichenkette an `python -c` übergeben — funktioniert und ist trotzdem die sch
Der Schnipsel ist für Editor, Linter und Testwerkzeug unsichtbar, ein Tippfehler darin
fällt erst zur Laufzeit auf, und übergeben lassen sich nur Zeichenketten.
Tragfähig ist stattdessen: **jeder Solver eine gewöhnliche Funktion mit lokalem Import**,
Tragfähig ist stattdessen: **jeder Solver eine gewöhnliche Funktion{idx:Konvexität!Funktion} mit lokalem Import**,
ausgeführt von einem `ProcessPoolExecutor` mit zwei Einstellungen, die zusammen die
Garantie ergeben:
@ -517,8 +517,8 @@ Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.
>
> | Ansatz | Zeilen für das Modell | Charakter |
> | --- | --- | --- |
> | `linprog` | 3 | Matrizen direkt übergeben. Kürzeste Variante, aber man muss selbst negieren und die Matrixform von Hand herstellen. |
> | `highspy` | ~15 | Alles explizit, inklusive CSR-Format der dünnbesetzten Matrix. Aufwendig — dafür volle Kontrolle und kein Overhead beim wiederholten Lösen. |
> | `linprog` | 3 | Matrizen direkt übergeben. Kürzeste Variante, aber man muss selbst negieren und die Matrixform{idx:Matrixform} von Hand herstellen. |
> | `highspy` | ~15 | Alles explizit, inklusive CSR-Format{idx:CSR-Format} der dünnbesetzten Matrix. Aufwendig — dafür volle Kontrolle und kein Overhead beim wiederholten Lösen. |
> | `cvxpy` | 4 | Liest sich wie die mathematische Formulierung. Prüft zusätzlich automatisch, ob das Problem konvex ist. Höchster Startaufwand pro Lauf (Kompilierung des Ausdrucksbaums). |
> | `ortools`/GLOP | ~6 | Bedingungen einzeln mit `Add()` — gut lesbar bei wenigen, mühsam bei vielen Restriktionen. |
>
@ -569,7 +569,7 @@ große LPs überhaupt lösbar sind.
| --- | --- | --- |
| Einmaliges kleines LP, Prototyp | `scipy.optimize.linprog` | Keine zusätzliche Abhängigkeit, 3 Zeilen |
| Portfolio, Risiko, alles Konvexe | **CVXPY** | Lesbarkeit + automatische Konvexitätsprüfung |
| Dienstpläne, Zuordnung, Reihenfolge | **CP-SAT** | Globale Constraints, Konfliktlernen |
| Dienstpläne, Zuordnung, Reihenfolge | **CP-SAT** | Globale Constraints{idx:Globale Constraints}, Konfliktlernen |
| Fahrzeugtouren | **OR-Tools Routing** | Fertige Metaheuristiken, jahrzehntelang optimiert |
| MILP mit Fixkosten, Kardinalität | `highspy` oder CP-SAT | Branch-and-Cut auf ökonomischer Struktur |
| 50 000+ Variablen, wiederholte Läufe | `highspy` oder Pyomo | Modellaufbau wird sonst zum Engpass |
@ -601,7 +601,7 @@ modell.kapazitaet = pyo.Constraint(modell.R, rule=kapazitaet)
```
Das ist das $\forall i \in I$ aus {ref:kap:einfuehrung}, unmittelbar in Code übersetzt: eine
Regel, angewandt auf jedes Element einer Menge. Pyomo kann außerdem, was CVXPY und OR-Tools
Regel, angewandt auf jedes Element einer Menge{idx:Konvexität!Menge}. Pyomo kann außerdem, was CVXPY und OR-Tools
nicht können — **nichtlineare und gemischt-ganzzahlig-nichtlineare** Modelle (MINLP) an
Solver wie Ipopt oder BONMIN übergeben (siehe {ref:kap:qp-nlp}).
@ -1225,7 +1225,7 @@ passende Empfehlungen.
> **🐛 Finde den Denkfehler: Der Solver, der angeblich dreimal schneller ist**
>
> Ein Team vergleicht zwei Bibliotheken für sein Zuordnungsproblem und schreibt ins
> Ein Team vergleicht zwei Bibliotheken für sein Zuordnungsproblem{idx:Zuordnungsproblem (1:1)} und schreibt ins
> Protokoll: *„CVXPY braucht 1,9 s, SciPy nur 0,6 s — wir setzen auf SciPy.“* Gemessen
> wurde so:
>
@ -1267,7 +1267,7 @@ passende Empfehlungen.
>
> **1. Sie sollen 300 Prüfungen auf 40 Räume und 12 Zeitfenster verteilen, mit Regeln wie
> „diese beiden Klausuren nicht gleichzeitig“. Welches Werkzeug passt?**
> (a) CVXPY — es prüft die Konvexität automatisch.
> (a) CVXPY — es prüft die Konvexität{idx:Konvexität} automatisch.
> (b) OR-Tools CP-SAT — diskrete Zuweisung mit logischen Regeln ist genau sein Gebiet.
> (c) `scipy.optimize.minimize` — es kann beliebige Zielfunktionen.
>
@ -1280,7 +1280,7 @@ passende Empfehlungen.
>
> **3. Was haben `scipy.optimize.linprog`, `highspy`, CVXPY (mit Standardeinstellung) und
> Pyomo (mit `appsi_highs`) gemeinsam?**
> (a) Sie implementieren jeweils einen eigenen Simplex-Algorithmus in Python.
> (a) Sie implementieren jeweils einen eigenen Simplex-Algorithmus{idx:Simplex-Algorithmus} in Python.
> (b) Sie sind Modellierungsschichten über demselben C++-Solver HiGHS — die Rechenleistung
> ist dieselbe, nur die Schreibweise unterscheidet sich.
> (c) Sie können alle sowohl konvexe als auch nicht-konvexe Probleme global lösen.

View file

@ -80,7 +80,7 @@ Monatsbericht steht.
> Ein Modell rechnet nicht aus, was man will, sondern was man aufgeschrieben hat. Die
> Differenz zwischen beidem ist die eigentliche Arbeit dieses Kapitels.
**Warum funktioniert das?** Weil eine Zielfunktion eine **vollständige** Aussage darüber ist,
**Warum funktioniert das?** Weil eine Zielfunktion{idx:Zielfunktion} eine **vollständige** Aussage darüber ist,
was zählt. Alles, was nicht darin steht, ist dem Modell gleichgültig — nicht „weniger
wichtig", sondern **wertlos**. Der Umsatzplan opfert den Deckungsbeitrag nicht aus Bosheit;
er sieht ihn schlicht nicht.
@ -96,7 +96,7 @@ Nach diesem Kapitel können Sie …
2. … die vier Fragen stellen, die aus einer Anforderung ein Modell machen.
3. … erkennen, dass ein Satz wie „Stammkunden dürfen wir nicht verlieren" mehrere Lesarten
hat, und den Unterschied in Euro beziffern.
4. … begründen, wann eine Regel hart und wann sie weich sein muss — und was eine harte Regel
4. … begründen, wann eine Regel hart{idx:Nebenbedingung!hart} und wann sie weich{idx:Nebenbedingung!weich} sein muss — und was eine harte Regel
im Betrieb anrichtet, wenn sie einmal nicht erfüllbar ist.
5. … einen Kennzahlen-Fehlanreiz benennen, bevor er in die Zielfunktion gerät.
6. … einen Satz aus einer Besprechung in den passenden Modellierungsbaustein
@ -112,7 +112,7 @@ Was aus einer Besprechung mitkommt, klingt meist so:
> *„Wir müssen die Maschinen besser auslasten. Und die Stammkunden dürfen wir nicht
> verlieren."*
Zwei Sätze, aus denen ein Modell werden soll. Sie enthalten: keine Entscheidungsvariable,
Zwei Sätze, aus denen ein Modell werden soll. Sie enthalten: keine Entscheidungsvariable{idx:Entscheidungsvariable},
keine messbare Zielgröße, keine Nebenbedingung. Sie enthalten nicht einmal genug, um zu
erkennen, *was* entschieden werden soll.
@ -142,7 +142,7 @@ sie übersprungen.
Das Programm dieses Kapitels arbeitet die vier Fragen an einer Auftragsannahme ab: 14
Anfragen verlangen 800 Maschinenstunden, verfügbar sind 300.
Es ist bewusst das **einfachste Modell des ganzen Buchs** — eine Binärvariable je Auftrag,
Es ist bewusst das **einfachste Modell des ganzen Buchs** — eine Binärvariable{idx:Binärvariable} je Auftrag,
eine Nebenbedingung. Alles, was sich zwischen den fünf Varianten ändert, ist die Frage.
```python
@ -547,7 +547,7 @@ Wer sie nicht stellt, entscheidet sie trotzdem — nur unbewusst, beim Tippen.
### Teil 3: Hart oder weich
Dieselbe Regel als Strafkosten je abgelehntem Stammkundenauftrag:
Dieselbe Regel als Strafkosten{idx:Strafkosten} je abgelehntem Stammkundenauftrag:
| Strafe je Auftrag | Deckungsbeitrag | abgelehnte Stammaufträge |
| ---: | ---: | ---: |
@ -639,26 +639,26 @@ es **bedeutet**, und welcher **Baustein** daraus wird. Die B-Nummern verweisen a
| Was gesagt wird | Was gemeint ist | Baustein |
| --- | --- | --- |
| „Wir haben nur 400 Stunden." | eine Obergrenze, die nicht überschritten werden darf | harte Kapazitätsgrenze $\le b$ ({ref:kap:lp}) |
| „Mehr als zwei Millionen gibt es nicht." | dasselbe, auf Geld | Budgetlimit (B27) |
| „Mehr als zwei Millionen gibt es nicht." | dasselbe, auf Geld | Budgetlimit{idx:Budgetlimit} (B27) |
| „Möglichst nicht über 40 Stunden." | eine Grenze, die im Notfall gebrochen werden darf | weiche Grenze mit Strafkosten (B8) |
| „Wir können nur ganze Kisten liefern." | die Menge ist nicht teilbar | Ganzzahligkeitsbedingung ({ref:kap:milp}) |
| „Wenn wir die Anlage nutzen, fallen Fixkosten an." | Kosten, die erst ab der ersten Einheit anfallen | Aktivierungsschalter (B1) |
| „Wir können nur ganze Kisten liefern." | die Menge{idx:Konvexität!Menge} ist nicht teilbar | Ganzzahligkeitsbedingung ({ref:kap:milp}) |
| „Wenn wir die Anlage nutzen, fallen Fixkosten an." | Kosten, die erst ab der ersten Einheit anfallen | Aktivierungsschalter{idx:Aktivierungsschalter (Modellierungsmuster)} (B1) |
| „Entweder gar nicht oder mindestens 50." | kein Betrieb im Kleinen | semikontinuierliche Variable (B2) |
| „Wenn wir A bauen, brauchen wir auch B." | eine Folgeverpflichtung | Implikation (B3) |
| „Wenn wir A bauen, brauchen wir auch B." | eine Folgeverpflichtung | Implikation{idx:Implikation (Modellierungsmuster)} (B3) |
| „Höchstens drei Positionen im Depot." | eine Obergrenze auf die **Anzahl**, nicht die Menge | Kardinalität (B6) |
| „Der Anteil muss mindestens 30 % betragen." | eine Bedingung auf ein **Verhältnis** | Verhältnis-Bedingung (B12) |
| „Der Anteil muss mindestens 30 % betragen." | eine Bedingung auf ein **Verhältnis** | Verhältnis-Bedingung{idx:Verhältnis-Bedingung} (B12) |
| „Jeder Auftrag genau einem Mitarbeiter." | eine vollständige, eindeutige Zuordnung | Zuordnung 1:1 (B23) |
| „Eine Maschine kann nur eines gleichzeitig." | zeitliche Ausschließlichkeit | Nichtüberlappung (B14) |
| „B kann erst nach A." | eine Reihenfolge | Vorrangbeziehung (B13) |
| „Eine Maschine kann nur eines gleichzeitig." | zeitliche Ausschließlichkeit | Nichtüberlappung{idx:Nichtüberlappung} (B14) |
| „B kann erst nach A." | eine Reihenfolge | Vorrangbeziehung{idx:Vorrangbeziehung} (B13) |
| „Höchstens fünf Tage am Stück." | eine Bedingung über ein **gleitendes Fenster** | gleitendes Fenster (B16) |
| „Umstellen kostet uns eine halbe Stunde." | reihenfolgeabhängige Kosten | Umrüstkosten (B17), Rüstzeit als Kapazität (B26) |
| „Was reinkommt, muss auch wieder raus." | Erhaltung an jedem Knoten | Flusserhaltung (B22) |
| „Umstellen kostet uns eine halbe Stunde." | reihenfolgeabhängige Kosten | Umrüstkosten{idx:Umrüstkosten (Modellierungsmuster)} (B17), Rüstzeit{idx:Rüstzeit} als Kapazität (B26) |
| „Was reinkommt, muss auch wieder raus." | Erhaltung an jedem Knoten | Flusserhaltung{idx:Flusserhaltung} (B22) |
| „Notfalls brechen wir die Regel." | die Bedingung ist weich, aber teuer | Schlupfvariablen (B18) |
| „Erst Termintreue, dann Kosten." | eine Rangfolge zwischen Zielen | hierarchische Ziele (B19) |
| „Es soll gerecht zugehen." | den Schlechtestgestellten verbessern | Min/Max in der Zielfunktion (B11) |
| „Was brächte uns eine zusätzliche Stunde?" | die Frage nach dem Grenzwert einer Ressource | Schattenpreis ({ref:kap:lp}, {ref:sec:praxisfallen-attribution}) |
| „Mit 95 % Sicherheit muss es halten." | eine Zusage auf eine **Quote**, nicht auf einen Mittelwert | Chance Constraint ({ref:sec:unsicherheit-chance-constraints}) |
| „Wir sichern uns gegen Schätzfehler ab." | gegen die ungünstigste Parameterlage planen | Worst-Case-Abzug (B21) |
| „Was brächte uns eine zusätzliche Stunde?" | die Frage nach dem Grenzwert einer Ressource | Schattenpreis{idx:Schattenpreis} ({ref:kap:lp}, {ref:sec:praxisfallen-attribution}) |
| „Mit 95 % Sicherheit muss es halten." | eine Zusage auf eine **Quote**, nicht auf einen Mittelwert | Chance Constraint{idx:Chance Constraint} ({ref:sec:unsicherheit-chance-constraints}) |
| „Wir sichern uns gegen Schätzfehler{idx:Schätzfehler} ab." | gegen die ungünstigste Parameterlage planen | Worst-Case-Abzug{idx:Robuste Optimierung!Worst-Case-Abzug} (B21) |
### Fünf Wendungen, bei denen die Übersetzung eine Entscheidung erzwingt
@ -690,7 +690,7 @@ jemandem erklärt werden muss, der das Modell nicht kennt:
| Optimalitätslücke von 2 % | „Besser als das hier geht es höchstens um 2 % — und die liegen unter unserer Datenungenauigkeit." |
| CVaR | „Wenn es schiefgeht, verlieren wir im Mittel so viel." (Der VaR sagt nur, **ob** es schiefgeht.) |
| EVPI | „Selbst eine perfekte Prognose wäre uns höchstens so viel wert." |
| Relaxation | „Wir haben die Regel probeweise gelockert, um zu sehen, was sie kostet." |
| Relaxation{idx:Relaxation} | „Wir haben die Regel probeweise gelockert, um zu sehen, was sie kostet." |
| Chance Constraint | „Der Plan hält in 95 von 100 Fällen — und der nächste Prozentpunkt kostet …" |
---

View file

@ -8,9 +8,9 @@ Programmieren zu tun hat.
| Frage | Kapitel | Was Sie danach können |
| --- | --- | --- |
| Was ist überhaupt ein Optimierungsmodell? | {ref:kap:einfuehrung} | Die vier Bausteine benennen — Variablen, Zielfunktion, Nebenbedingungen, Daten — und sie in einem fremden Problem wiedererkennen |
| Warum rechnet der Solver falsch, obwohl das Modell stimmt? | {ref:kap:fundament} | Konvexität beurteilen, den zulässigen Bereich als Polyeder sehen, schlechte Konditionierung erkennen und beheben |
| Welche Bibliothek, und warum vertragen sie sich nicht? | {ref:kap:oekosystem} | Solver, Bindings und Modellierungsschichten unterscheiden — und den HiGHS-Symbolkonflikt zwischen `ortools` und `highspy` umgehen |
| Was ist überhaupt ein Optimierungsmodell? | {ref:kap:einfuehrung} | Die vier Bausteine benennen — Variablen, Zielfunktion{idx:Zielfunktion}, Nebenbedingungen, Daten — und sie in einem fremden Problem wiedererkennen |
| Warum rechnet der Solver falsch, obwohl das Modell stimmt? | {ref:kap:fundament} | Konvexität{idx:Konvexität} beurteilen, den zulässigen Bereich als Polyeder{idx:Polyeder} sehen, schlechte Konditionierung erkennen und beheben |
| Welche Bibliothek, und warum vertragen sie sich nicht? | {ref:kap:oekosystem} | Solver, Bindings und Modellierungsschichten unterscheiden — und den HiGHS{idx:HiGHS}-Symbolkonflikt zwischen `ortools` und `highspy` umgehen |
| Wie wird aus einem Satz ein Modell? | {ref:kap:modellierung} | Die vier Fragen stellen, harte von weichen Regeln trennen, einen Besprechungssatz übersetzen ({ref:sec:modellierung-glossar}) |
## Die Reihenfolge ist die Aussage

View file

@ -11,12 +11,12 @@ Fragen:
> **🎯 Merksatz zum Diagramm**
> Die erste Frage ist die wichtigste — und sie lautet **nicht** „welcher Solver ist der
> schnellste?“, sondern „welche **Struktur** hat mein Problem?“. Ein Zuordnungsproblem
> schnellste?“, sondern „welche **Struktur** hat mein Problem?“. Ein Zuordnungsproblem{idx:Zuordnungsproblem (1:1)}
> braucht keinen MILP-Solver, sondern eine Zeile `linear_sum_assignment`. Ein Dienstplan
> gehört nicht in ein LP, sondern zu CP-SAT. Wer die Struktur erkennt, hat die halbe
> gehört nicht in ein LP, sondern zu CP-SAT{idx:CP-SAT}. Wer die Struktur erkennt, hat die halbe
> Laufzeit gespart, bevor die erste Zeile Code geschrieben ist.
Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare Zielfunktion, unsichere Daten,
Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare Zielfunktion{idx:Zielfunktion}, unsichere Daten,
Entscheidungen über mehrere Zeitstufen —, dann ist {ref:teil:nichtlinear} der richtige
Ort. Dort steht ein zweites Diagramm für genau diese Fälle.
@ -27,14 +27,14 @@ Ort. Dort steht ein zweites Diagramm für genau diese Fälle.
> **📌 Kapitel auf einen Blick**
>
> **Worum geht es?** Um das Kernverfahren des Operations Research. Wir bauen den
> Simplex-Algorithmus selbst — nicht um ihn zu verwenden, sondern um zu verstehen, woher
> Simplex-Algorithmus{idx:Simplex-Algorithmus} selbst — nicht um ihn zu verwenden, sondern um zu verstehen, woher
> **Schattenpreise** kommen. Sie sind das wertvollste Nebenprodukt jeder Optimierung.
>
> **Voraussetzungen:** {ref:kap:fundament} (Matrixform, Polyeder, Ecken).
> **Voraussetzungen:** {ref:kap:fundament} (Matrixform{idx:Matrixform}, Polyeder, Ecken).
>
> **Danach können Sie:** Ein LP in Standardform bringen, den Simplex von Hand rechnen,
> Dualwerte interpretieren, daraus wirtschaftliche Handlungsempfehlungen ableiten — und
> erkennen, wann ein Schattenpreis eine Entscheidung **nicht** tragen darf.
> erkennen, wann ein Schattenpreis{idx:Schattenpreis} eine Entscheidung **nicht** tragen darf.
>
> **Zeitbedarf:** ca. 7 Stunden — das umfangreichste Kapitel von {ref:teil:kernverfahren}.
>
@ -124,13 +124,13 @@ Nach diesem Kapitel können Sie …
1. … Schlupfvariablen einführen und ein Ungleichungssystem in ein Gleichungssystem
überführen.
2. … eine Simplex-Iteration von Hand rechnen: Pivotspalte, Pivotzeile, Basistausch.
2. … eine Simplex-Iteration von Hand rechnen: Pivotspalte{idx:Simplex-Algorithmus!Pivotspalte}, Pivotzeile, Basistausch.
3. … erklären, warum das Verfahren terminiert und warum es das Optimum findet.
4. … das duale Problem zu einem gegebenen primalen aufstellen.
5. … **Schattenpreise**{idx:Schattenpreis} korrekt auslesen und interpretieren — inklusive der Vorzeichenfalle,
die einer der häufigsten Fehler bei der Sensitivitätsanalyse ist.
5. … **Schattenpreise**{idx:Schattenpreis} korrekt auslesen und interpretieren — inklusive der Vorzeichenfalle{idx:Schattenpreis!Vorzeichenfalle},
die einer der häufigsten Fehler bei der Sensitivitätsanalyse{idx:Sensitivitätsanalyse} ist.
6. … aus einer Sensitivitätsanalyse eine begründete Kaufentscheidung ableiten.
7. … ein Modell auf **Entartung** prüfen und statt eines nicht eindeutigen Schattenpreises
7. … ein Modell auf **Entartung**{idx:Entartung} prüfen und statt eines nicht eindeutigen Schattenpreises
dessen belastbare Spanne berechnen.
8. … erklären, warum Zulässigkeit immer gegen eine Toleranz und nie mit `== 0` geprüft wird.
@ -167,7 +167,7 @@ $$
| $s_i > 0$ | Es sind $s_i$ Einheiten **übrig** | nicht-bindende Nebenbedingung, Reserve |
> **🎯 Merksatz**
> Die Schlupfvariable ist kein Rechentrick, sondern eine Messgröße: Sie sagt Ihnen, welche
> Die Schlupfvariable{idx:Schlupfvariable} ist kein Rechentrick, sondern eine Messgröße: Sie sagt Ihnen, welche
> Ressource Ihr Geschäft begrenzt. Und nur wo sie null ist, kann es sich lohnen, Kapazität
> nachzukaufen.
@ -182,12 +182,12 @@ Ecke zu Ecke — und zwar immer in Richtung Verbesserung.
![Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über
(0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der
ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der
Zielfunktion, der Pfeil ihr Gradient $\mathbf{c}$. Erzeugt von
Zielfunktion, der Pfeil ihr Gradient{idx:Gradient} $\mathbf{c}$. Erzeugt von
`bilder_04/erzeuge_polyeder.py`.](bilder_04/kap_lp_simplex_ecken.svg)
**Was Sie in der Abbildung sehen.** Durch jede besuchte Ecke läuft genau eine Niveaulinie —
die Linien sind nicht gewählt, sondern die Zielwerte 0 €, 2.000 € und 2.375 € der drei Ecken.
Der Simplex schiebt diese Linie in Richtung des Pfeils so weit hinaus, wie das Polyeder es
Der Simplex schiebt diese Linie in Richtung des Pfeils so weit hinaus, wie das Polyeder{idx:Polyeder} es
zulässt. Dass er dabei an einer Ecke anhält und nicht mittendrin, ist keine Eigenheit des
Verfahrens, sondern der Fundamentalsatz: Weiter geht es nur noch hinaus.
@ -506,7 +506,7 @@ und 31,25 €.
> theoretisch endlos kreisen. In der Praxis extrem selten; Bland's Regel verhindert es
> beweisbar.
> * **Den Simplex selbst produktiv einsetzen.** Diese Implementierung ist ein Lehrmodell.
> HiGHS ist um Größenordnungen schneller und numerisch stabiler.
> HiGHS{idx:HiGHS} ist um Größenordnungen schneller und numerisch stabiler.
---
@ -938,7 +938,7 @@ Deckungsbeitrag, also 15,33 € Reingewinn.
> 2. **Numerische Gegenprobe:** Das Modell wird mit $b_3 + 1$ neu gelöst; der gemessene
> Zuwachs muss dem Schattenpreis entsprechen. Diese Prüfung ist unabhängig von jeder
> Vorzeichenkonvention und deshalb der zuverlässigste Test überhaupt.
> 3. **Komplementärer Schlupf:** $s_i \cdot y_i = 0$ muss für alle $i$ gelten.
> 3. **Komplementärer Schlupf{idx:Komplementärer Schlupf}:** $s_i \cdot y_i = 0$ muss für alle $i$ gelten.
>
> **Übernehmen Sie dieses Muster in eigene Projekte.** Der Aufwand ist eine Handvoll Zeilen,
> der Nutzen ist die Gewissheit, dass Ihre Handlungsempfehlung nicht das Gegenteil des
@ -992,7 +992,7 @@ Das ist keine theoretische Sorge. Dasselbe Modell, zwei Verfahren desselben Solv
| Verfahren | Schattenpreise | Was das Management daraus liest |
| --- | --- | --- |
| Dual Simplex | $(0{,}33;\ 0{,}33;\ 0)$ | „Fräse und Schleiferei sind die Engpässe, die Prüfung ist wertlos.“ |
| Dual Simplex{idx:Dual Simplex} | $(0{,}33;\ 0{,}33;\ 0)$ | „Fräse und Schleiferei sind die Engpässe, die Prüfung ist wertlos.“ |
| Innere-Punkte-Verfahren | $(0;\ 0;\ 1{,}00)$ | „Die Prüfung ist der Engpass, Fräse und Schleiferei sind wertlos.“ |
**Beide Zeilen sind mathematisch korrekt.** Sie widersprechen sich trotzdem vollständig.
@ -1021,7 +1021,7 @@ nicht `== 0.0`. Wer exakt vergleicht, übersieht ausgerechnet die Engpässe, die
### Die ehrliche Auskunft: Schattenpreis-Spannen
Was tut man also, wenn das Modell entartet ist? Man meldet keinen Einzelwert, sondern die
**Spanne**. Die Menge aller optimalen Dualvektoren ist selbst ein Polyeder:
**Spanne**. Die Menge{idx:Konvexität!Menge} aller optimalen Dualvektoren ist selbst ein Polyeder:
$$
\mathbf{A}^\top\mathbf{y} \ge \mathbf{c},\qquad \mathbf{y} \ge \mathbf{0},
@ -1318,7 +1318,7 @@ $$\max\ 5x_1 + 4x_2 \quad \text{u. d. N.}\quad 6x_1+4x_2 \le 24,\quad x_1+2x_2 \
Geben Sie jedes Zwischentableau an und lesen Sie am Ende Lösung **und** Schattenpreise ab.
Prüfen Sie mit `scipy.optimize.linprog`.
**Aufgabe ⭐⭐ — Duales Problem aufstellen.**
**Aufgabe ⭐⭐ — Duales Problem{idx:Duales Problem} aufstellen.**
Stellen Sie zum Modell aus der Aufgabe *Simplex von Hand* das duale Problem auf, lösen Sie es mit `linprog` und
weisen Sie den starken Dualitätssatz numerisch nach.
@ -1367,7 +1367,7 @@ $$\max\ 3x_1+2x_2 \quad\text{u. d. N.}\quad x_1+x_2 \ge 4,\quad x_1+3x_2 \le 12,
> (a) Der Schattenpreis ist $y_i = \partial Z^* / \partial b_i$. Welche Eigenschaft einer
> Ableitung übersieht die Hochrechnung auf 2 000 Stunden?
>
> (b) Skizzieren Sie, wie der zusätzliche Deckungsbeitrag als Funktion der zusätzlichen
> (b) Skizzieren Sie, wie der zusätzliche Deckungsbeitrag als Funktion{idx:Konvexität!Funktion} der zusätzlichen
> Ofenstunden tatsächlich verläuft. Warum ist er nicht linear — und in welche Richtung
> liegt der Fehler?
>
@ -1444,7 +1444,7 @@ $$\max\ 3x_1+2x_2 \quad\text{u. d. N.}\quad x_1+x_2 \ge 4,\quad x_1+3x_2 \le 12,
**Ausblick.** {ref:kap:milp} bricht mit der Annahme beliebiger Teilbarkeit: Was, wenn man nur
ganze Maschinen kaufen kann? Wir werden sehen, dass Runden der LP-Lösung nicht nur ungenau,
sondern grundsätzlich falsch ist — und lernen Branch-and-Bound kennen.
sondern grundsätzlich falsch ist — und lernen Branch-and-Bound{idx:Branch-and-Bound} kennen.
---

View file

@ -1,4 +1,4 @@
# Kapitel: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound {#kap:milp}
# Kapitel: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound{idx:Branch-and-Bound} {#kap:milp}
> **📌 Kapitel auf einen Blick**
>
@ -7,10 +7,10 @@
> **falsch** ist, und lernen die Modellierungstricks, mit denen sich logische Regeln in
> lineare Ungleichungen übersetzen lassen.
>
> **Voraussetzungen:** {ref:kap:lp} (LP, Relaxation, Schranken).
> **Voraussetzungen:** {ref:kap:lp} (LP, Relaxation{idx:Relaxation}, Schranken).
>
> **Danach können Sie:** Fixkosten, Entweder-Oder, Wenn-Dann und Kardinalitätsgrenzen
> modellieren, ein MILP mit CP-SAT oder HiGHS lösen, den MIP-Gap als Zusage an den
> modellieren, ein MILP mit CP-SAT{idx:CP-SAT} oder HiGHS lösen, den MIP-Gap{idx:MIP-Gap} als Zusage an den
> Auftraggeber lesen — und ein Ergebnis erkennen, das nur deshalb gut aussieht, weil
> das Big-M zu groß gewählt war.
>
@ -68,7 +68,7 @@
> MILP 882.00 EUR Mengen [10. 0. 90.] Schalter [ 1. -0. 1. ]
> ```
**Sehen Sie sich die Schalter der ersten Zeile an: 0,143.** Die LP-Relaxation eröffnet
**Sehen Sie sich die Schalter der ersten Zeile an: 0,143.** Die LP-Relaxation{idx:LP-Relaxation} eröffnet
Lager Mitte zu 14,3 % — und zahlt dafür nur 14,3 % der Fixkosten. So etwas gibt es in der
Wirklichkeit nicht. Ein Lager ist offen oder zu.
@ -107,10 +107,10 @@ Nach diesem Kapitel können Sie …
Kardinalität).
4. … begründen, warum $M$ so klein wie möglich gewählt werden muss.
5. … ein Portfolioproblem mit Ordergebühren und Höchstzahl an Positionen lösen.
6. … **Incumbent, Schranke und MIP-Gap** unterscheiden und aus einem abgebrochenen
6. … **Incumbent{idx:Branch-and-Bound!Incumbent}, Schranke und MIP-Gap** unterscheiden und aus einem abgebrochenen
Solverlauf eine belastbare Aussage ableiten.
7. … alle Solver-Statusfälle explizit behandeln, statt `OPTIMAL` vorauszusetzen.
8. … einen **Warm-Start** setzen — und messen, ob er auf Ihrer Problemklasse überhaupt
8. … einen **Warm-Start**{idx:Warm-Start} setzen — und messen, ob er auf Ihrer Problemklasse überhaupt
etwas bringt.
---
@ -130,7 +130,7 @@ Der verlockende Gedanke lautet: *„Wir lösen das Problem kontinuierlich und ru
Eine verbreitete, aber unbelegte Behauptung dazu lautet, man verliere dadurch „oft
2050 % des Gewinns“. Rechnen wir es nach, statt es zu behaupten.
![Links die Handrechnung von unten als Bild: Die Zielfunktion läuft parallel zur
![Links die Handrechnung von unten als Bild: Die Zielfunktion{idx:Zielfunktion} läuft parallel zur
Restriktion, also ist die ganze Kante optimal. Aufrunden auf $(1;1)$ führt aus dem zulässigen
Bereich heraus, Abrunden auf $(0;0)$ auf den Wert null. Rechts die Messung aus
`Runden_Gegenbeispiel.py` über je 200 Zufallsinstanzen. Erzeugt von
@ -384,7 +384,7 @@ also das, was der Solver sieht, bevor $y$ ganzzahlig wird. Erzeugt von
beiden blauen Punkte stimmen überein. Verschieden ist nur, was dazwischen erlaubt ist — und
rechts genügt bereits $y = 0{,}08$, um die volle Kapazität freizugeben.
### Muster 1 — Fixkosten / Aktivierungsschalter
### Muster 1 — Fixkosten / Aktivierungsschalter{idx:Aktivierungsschalter (Modellierungsmuster)}
*Regel:* Wird Maschine $j$ genutzt ($x_j > 0$), fallen Rüstkosten $F_j$ an.
@ -409,7 +409,7 @@ $$
* $y=1$: erste Bedingung aktiv, zweite durch $+M$ praktisch außer Kraft.
* $y=0$: umgekehrt.
### Muster 3 — Wenn-Dann (Implikation)
### Muster 3 — Wenn-Dann (Implikation{idx:Implikation (Modellierungsmuster)})
*Regel:* Wenn $y_1 = 1$, dann muss auch $y_2 = 1$ sein.
@ -467,7 +467,7 @@ Dieses Muster{idx:Semikontinuierliche Variable} brauchen wir gleich für die Min
---
## Beispiel: Das Rucksackproblem {#sec:milp-beispiel-das-rucksackproblem}
## Beispiel: Das Rucksackproblem{idx:Rucksackproblem} {#sec:milp-beispiel-das-rucksackproblem}
Bevor wir zum Portfolio kommen, das klassische Einstiegsproblem{idx:Rucksackproblem} — kurz, verständlich und
überall wiederzuerkennen.
@ -893,7 +893,7 @@ Abnahmepruefung: alle Nebenbedingungen geprueft und eingehalten.
> Kardinalitätsgrenze läge alles im Bestwert (13 %); die Restriktionen kosten 15 % des
> theoretischen Ertrags. Genau diese Zahl braucht man, wenn man mit dem Risikomanagement
> über die Sinnhaftigkeit einer Regel diskutiert: „Die Obergrenze von 40 % je Position
> kostet uns 1 950 € pro Jahr — ist uns die Diversifikation das wert?“ Das ist eine
> kostet uns 1 950 € pro Jahr — ist uns die Diversifikation{idx:Diversifikation} das wert?“ Das ist eine
> beantwortbare Frage; „wir sollten breiter streuen“ ist es nicht.
---
@ -1828,7 +1828,7 @@ funktioniert immer.
Entweder-Oder, Wenn-Dann, Kardinalität, semikontinuierlich.
* **$M$ so klein wie möglich** — nicht nur wegen der Laufzeit. Ein übergroßes $M$ kann
Binärvariablen bei $10^{-8}$ hängen lassen, wo sie dem Solver als 0 gelten: Anlagen
produzieren, ohne dass ihre Fixkosten je verbucht werden (*Trickle Flow*).
produzieren, ohne dass ihre Fixkosten je verbucht werden (*Trickle Flow*{idx:Trickle Flow}).
* **`OPTIMAL` ist nicht der Normalfall.** Werten Sie jeden Status aus und lesen Sie den
**MIP-Gap** — er ist eine Garantie („höchstens 2,3 % vom Optimum“), keine Schätzung. Der
Beweis der Optimalität kostet meist ein Vielfaches dessen, was das Finden der optimalen

View file

@ -1,4 +1,4 @@
# Kapitel: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung {#kap:cpsat}
# Kapitel: Constraint Programming mit CP-SAT{idx:CP-SAT} — Logik, Scheduling und Zuweisung {#kap:cpsat}
> **📌 Kapitel auf einen Blick**
>
@ -9,7 +9,7 @@
> **Voraussetzungen:** {ref:kap:milp} (Binärvariablen, logische Bedingungen).
>
> **Danach können Sie:** Zuweisungs-, Scheduling- und Reihenfolgeprobleme mit CP-SAT
> modellieren, globale Constraints einsetzen, weiche Ziele über Strafkosten steuern — und
> modellieren, globale Constraints einsetzen, weiche Ziele über Strafkosten{idx:Strafkosten} steuern — und
> alle fünf Solver-Antworten auseinanderhalten, statt nur auf `OPTIMAL` zu prüfen.
>
> **Zeitbedarf:** ca. 6,5 Stunden.
@ -105,7 +105,7 @@ Nach diesem Kapitel können Sie …
7. … begründen, warum `num_workers = 1` für einen reproduzierbaren Lauf nötig ist und ein
fester `random_seed` allein nicht genügt — und was ein Test deshalb prüfen darf.
8. … begründen, warum ein Strafgewicht ein **Wechselkurs** ist, und welche Regeln deshalb
niemals in die Zielfunktion gehören.
niemals in die Zielfunktion{idx:Zielfunktion} gehören.
---
@ -118,7 +118,7 @@ Während LP und MILP auf algebraischen Gleichungen und Simplex-Matrizen beruhen,
| --- | --- | --- |
| Variablen | reell oder ganzzahlig, mit Schranken | diskrete **Wertebereiche** (*domains*) |
| Bedingungen | lineare Ungleichungen | beliebige logische Relationen |
| Suchprinzip | Relaxation + Verzweigen + Schranken | **Propagation** + Verzweigen + Konfliktlernen |
| Suchprinzip | Relaxation{idx:Relaxation} + Verzweigen + Schranken | **Propagation** + Verzweigen + Konfliktlernen |
| Stärke | ökonomische Modelle, Kosten, Kapazitäten | Zuordnung, Reihenfolge, Zeitfenster, Regeln |
| Schwäche | viele logische Regeln → viele Big-M | reine Kostenoptimierung über Kontinuum |
@ -126,11 +126,11 @@ Der **CP-SAT-Solver**{idx:CP-SAT-Solver} von Google OR-Tools verbindet Constrain
**SAT-Techniken**{idx:SAT (Boolean Satisfiability)} (*Boolean Satisfiability*, Erfüllbarkeitsproblem der Aussagenlogik),
insbesondere **CDCL**{idx:CDCL} (*Conflict-Driven Clause Learning*, konfliktgetriebenes Klausellernen).
Er gehört zu den weltweit leistungsfähigsten Engines für Schichtpläne, Vertretungsplanung,
Projektterminierung und Job-Shop-Scheduling.
Projektterminierung und Job-Shop-Scheduling{idx:Job-Shop-Scheduling}.
### Was Propagation leistet
![CP-SAT Propagation und Conflict Learning](bilder_04/kap06_cp_sat_propagation.svg)
![CP-SAT Propagation und Conflict Learning{idx:CDCL!Conflict Learning}](bilder_04/kap06_cp_sat_propagation.svg)
> **✏️ Handrechnung: Propagation von Hand**
>
@ -246,7 +246,7 @@ if __name__ == "__main__":
---
## Globale Constraints — die Bausteine von CP-SAT {#sec:cpsat-globale-constraints-die-bausteine-von-cp}
## Globale Constraints{idx:Globale Constraints} — die Bausteine von CP-SAT {#sec:cpsat-globale-constraints-die-bausteine-von-cp}
CP-SAT stellt mächtige Makros{idx:Globale Constraints} bereit, die in MILP hunderte Big-M-Ungleichungen erfordern
würden. Jedes davon hat einen spezialisierten, hocheffizienten Propagator.
@ -604,9 +604,9 @@ Suchstatistik: 113 Verzweigungen, 3 Konflikte, 0.006 s
> CP-SAT-Konstrukte, die in MILP mehrere Big-M-Ungleichungen bräuchten. Die Spannweite
> $\max - \min$ zu minimieren ist ein Standardrezept für „gleichmäßig verteilen“.
>
> **3. Kostenzerlegung.** Der Block „Woraus bestehen die Strafkosten?“ ist kein Beiwerk. In
> **3. Kostenzerlegung{idx:Kostenzerlegung}.** Der Block „Woraus bestehen die Strafkosten?“ ist kein Beiwerk. In
> der Praxis ist die erste Frage jedes Anwenders: *„Warum bekomme **ich** die Stunde?“* Wer
> darauf keine Antwort hat, dessen System wird nicht benutzt ({ref:kap:praxisfallen}, *Explainable OR*).
> darauf keine Antwort hat, dessen System wird nicht benutzt ({ref:kap:praxisfallen}, *Explainable OR*{idx:Explainable OR}).
>
> **4. Statusauswertung ohne CP-SAT-Vokabular.** Nach `Solve()` steht nur eine einzige Zeile
> mit einer CP-SAT-Konstante darin:
@ -615,7 +615,7 @@ Suchstatistik: 113 Verzweigungen, 3 Konflikte, 0.006 s
> ```
> Danach heißen die Fälle `status.modellfehler` (die Regeln widersprechen sich — das Modell
> muss geändert werden) und `status.brauchbar` (es liegt etwas Ausführbares vor). Das ist
> **dieselbe** Fallunterscheidung wie bei HiGHS in {ref:kap:milp} und bei GLOP in
> **dieselbe** Fallunterscheidung wie bei HiGHS{idx:HiGHS} in {ref:kap:milp} und bei GLOP in
> {ref:kap:lp}; nur die eine Übersetzerzeile weiß, welcher Solver gerechnet hat. Wer den
> Auswertungscode direkt gegen `cp_model.INFEASIBLE` schreibt, kann ihn bei einem
> Solverwechsel wegwerfen — {ref:sec:praxisfallen-solverwechsel} führt genau diesen Wechsel
@ -625,7 +625,7 @@ Suchstatistik: 113 Verzweigungen, 3 Konflikte, 0.006 s
>
> * **Zu viele harte Regeln.** Jede zusätzliche harte Bedingung erhöht das Risiko von
> `INFEASIBLE`. Faustregel: Hart ist nur, was rechtlich oder physikalisch unmöglich ist.
> Alles andere wird weich mit hoher Strafe — dann liefert der Solver den „am wenigsten
> Alles andere wird weich{idx:Nebenbedingung!weich} mit hoher Strafe — dann liefert der Solver den „am wenigsten
> schlechten“ Plan statt einer Fehlermeldung.
> * **Strafgewichte willkürlich wählen.** Die Gewichte (50, 80, 30) definieren implizit eine
> Werteordnung: „Ein Freistundenloch ist so schlimm wie 1,6 Stunden Vorbelastung.“
@ -834,7 +834,7 @@ theoretischen Ideal entfernt sein können, noch bevor der Solver fertig war.
> **💡 Warum Intervallvariablen so wertvoll sind**
>
> Eine Intervallvariable bündelt drei Größen (Start, Dauer, Ende) und garantiert intern
> Eine Intervallvariable{idx:Intervallvariable} bündelt drei Größen (Start, Dauer, Ende) und garantiert intern
> `start + dauer == ende`. Der eigentliche Gewinn ist aber `AddNoOverlap`: Diese eine Zeile
> ersetzt für $k$ Gänge auf einer Maschine $\binom{k}{2}$ Entweder-Oder-Konstruktionen mit
> je zwei Big-M-Ungleichungen und einer Binärvariablen. Bei 10 Gängen sind das 45
@ -1123,7 +1123,7 @@ random_seed = 1 in JEDEM Lauf, 4 Wiederholungen je Arbeiterzahl.
nicht achtmal, sondern **gut zwölfmal** schneller als einer. Das sieht nach Messfehler aus, ist aber die
korrekte Beschreibung dessen, was CP-SAT tut: Es lässt **nicht** achtmal dieselbe Suche auf
verschiedenen Kernen laufen, sondern **verschiedene Strategien nebeneinander** — Suche mit
LP-Relaxation, Suche mit anderen Verzweigungsregeln, lokale Suche —, und die teilen
LP-Relaxation{idx:LP-Relaxation}, Suche mit anderen Verzweigungsregeln, lokale Suche —, und die teilen
einander ihre Schranken mit. Der zweite Arbeiter ist deshalb nicht „der zweite Kern“,
sondern ein anderes Verfahren, das zufällig auch rechnet.
@ -1146,7 +1146,7 @@ entscheidet die Uhr, und die ist bei jedem Lauf anders.
> **⚠️ `random_seed` allein macht nichts reproduzierbar**
> Ein häufiger Irrtum: „Ich habe doch einen Seed gesetzt.“ Für einen Zufallsgenerator
> stimmt das. Für einen **parallelen** Solver nicht — dort ist die zweite Zufallsquelle die
> Ausführungsreihenfolge, und die lässt sich nicht mit einem Parameter festnageln.
> Ausführungsreihenfolge, und die lässt sich nicht mit einem Parameter{idx:Parameter (Modell)} festnageln.
> Reproduzierbar wird der Lauf erst mit `num_workers = 1` **und** Seed. Wer nur eines von
> beidem setzt, hat einen Test, der gelegentlich grundlos rot wird.
@ -1485,7 +1485,7 @@ genau die Information, die zur Fehlersuche noetig waere.
>
> | Stelle | Was passiert | Warum es zählt |
> | --- | --- | --- |
> | `werte_aus()` | übersetzt Status in **Handlungsempfehlung** | Genau diese Funktion fehlt in den meisten Projekten. Sie ist kurz — und der Unterschied zwischen einem Prototyp und einem System, das nachts ohne Aufsicht läuft. |
> | `werte_aus()` | übersetzt Status in **Handlungsempfehlung** | Genau diese Funktion{idx:Konvexität!Funktion} fehlt in den meisten Projekten. Sie ist kurz — und der Unterschied zwischen einem Prototyp und einem System, das nachts ohne Aufsicht läuft. |
> | `hat_loesung = status in (OPTIMAL, FEASIBLE)` | die eigentliche Frage | Nicht „war der Solver erfolgreich?“, sondern „habe ich etwas in der Hand, das ich ausführen kann?“ |
> | `BestObjectiveBound()` | die bewiesene Schranke | Ohne sie ist `FEASIBLE` eine nutzlose Angabe. Mit ihr wird daraus „höchstens 1,27 % vom Optimum“. |
> | Läufe [2] und [3] | 0,5 s gegen 10 s | Der Gap fällt von 1,27 % auf 0,14 %. Zwanzigfache Rechenzeit für gut ein Prozent — die Entscheidung, ob sich das lohnt, trifft die Anwendung, nicht der Solver. |
@ -1525,9 +1525,9 @@ Erweitern Sie das Vertretungssystem um:
(b) Herr Bauer und Frau Koch sollen nicht beide am selben Tag eingesetzt werden.
(c) Wer Slot 1 übernimmt, soll bevorzugt auch Slot 2 bekommen (Belohnung statt Strafe).
**Aufgabe ⭐⭐ — Infeasibility erzeugen und diagnostizieren.**
**Aufgabe ⭐⭐ — Infeasibility{idx:Infeasibility} erzeugen und diagnostizieren.**
Setzen Sie `MAX_VERTRETUNGEN = 0`. Was meldet das Programm? Bauen Sie anschließend eine
**Schlupfvariable** je Slot ein („Stunde fällt aus“, Strafe 10 000) und beobachten Sie, wie
**Schlupfvariable**{idx:Schlupfvariable} je Slot ein („Stunde fällt aus“, Strafe 10 000) und beobachten Sie, wie
der Solver statt einer Fehlermeldung einen Notfallplan liefert. Welche Stunde lässt er
ausfallen und warum?
@ -1785,7 +1785,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
> Der Plan ist mit dem vorhandenen Personal nicht zulässig zu machen.
> * **Gewichte in verschiedenen Einheiten mischen.** Euro neben „Unzufriedenheitspunkten“
> neben Minuten ergibt eine Zielfunktion, die niemand interpretieren kann — und deren
> Konditionszahl obendrein leidet ({ref:sec:fundament-kondition}).
> Konditionszahl{idx:Konditionszahl} obendrein leidet ({ref:sec:fundament-kondition}).
> * **Nur ein Gewicht ausprobieren.** Ein einziger Lauf sagt nichts darüber, ob Sie nahe an
> einem Kipppunkt stehen. Zwischen den Gewichten 5 und 10 ändert sich der Plan oben
> erheblich.
@ -1830,7 +1830,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
1. Was macht Constraint Propagation, und warum ist sie effizienter als Ausprobieren?
2. Welchen Vorteil hat `AddAllDifferent(x)` gegenüber $\binom{n}{2}$ Einzelbedingungen?
3. Was ist eine Intervallvariable, und welche Bedingung garantiert sie automatisch?
4. Warum sollten möglichst wenige Bedingungen hart formuliert werden?
4. Warum sollten möglichst wenige Bedingungen hart{idx:Nebenbedingung!hart} formuliert werden?
5. Warum ist die Aufschlüsselung der Strafkosten in der Praxis wichtiger als der Zielwert
6. Sie setzen `random_seed = 1`, bekommen aber trotzdem bei jedem Lauf einen anderen Plan.
Woran liegt das, und was hilft?

View file

@ -85,11 +85,11 @@ mehr.
> nacheinander. Bei vier Monteuren kostet die gierige Regel 8 %; bei vierzig kostet sie
> regelmäßig ein Vielfaches.
**Warum funktioniert das?** Weil das Zuordnungsproblem eine besondere Struktur hat: Seine
**Warum funktioniert das?** Weil das Zuordnungsproblem{idx:Zuordnungsproblem (1:1)} eine besondere Struktur hat: Seine
Nebenbedingungsmatrix ist **total unimodular**. Das bedeutet — wir kommen in
{ref:sec:graphen-bipartites-matching-das-zuordnungsproblem} darauf zurück —, dass die
LP-Relaxation von ganz allein ganzzahlige Lösungen liefert. Man braucht hier also weder
Branch-and-Bound noch Binärvariablen. Dieselbe Eigenschaft macht auch Flussprobleme so
LP-Relaxation{idx:LP-Relaxation} von ganz allein ganzzahlige Lösungen liefert. Man braucht hier also weder
Branch-and-Bound{idx:Branch-and-Bound} noch Binärvariablen. Dieselbe Eigenschaft macht auch Flussprobleme so
angenehm lösbar, und damit beginnt das Kapitel.
---
@ -102,7 +102,7 @@ Nach diesem Kapitel können Sie …
2. … den **Flusserhaltungssatz**{idx:Flusserhaltung} aufstellen und seine Bedeutung erklären.
3. … begründen, warum Zuordnungsprobleme **ohne** Ganzzahligkeitsbedingung ganzzahlig lösbar
sind (totale Unimodularität{idx:Totale Unimodularität}).
4. … ein Vehicle Routing Problem mit Kapazitäten und Zeitfenstern mit OR-Tools lösen.
4. … ein Vehicle Routing Problem mit Kapazitäten und Zeitfenstern mit OR-Tools{idx:OR-Tools} lösen.
5. … einschätzen, wann ein spezialisierter Algorithmus einem allgemeinen MILP überlegen ist.
6. … begründen, warum eine gierige Zuordnung systematisch schlechter ist als eine
gemeinsame Optimierung.
@ -128,7 +128,7 @@ mathematische Fundament für Transportketten, Liquiditätsrouting und Datenverte
Route über den Umschlag läuft voll (dick, amber), die Kante Werk B → Umschlag bleibt
ungenutzt (gestrichelt) — und trotzdem muss die teure Direktkante Werk B → Kunde 2 für 6 €
bedient werden, weil der Umschlagweg dorthin schon ausgelastet ist. An den Knoten stehen die
Dualwerte der Flusserhaltung. Erzeugt von
Dualwerte der Flusserhaltung{idx:Flusserhaltung}. Erzeugt von
`bilder_04/erzeuge_min_cost_flow.py`.](bilder_04/kap_graphen_min_cost_flow.svg)
Sei $x_{ij} \ge 0$ der Fluss über Kante $(i,j)$, $c_{ij}$ die Kosten je Einheit und
@ -307,7 +307,7 @@ schon bei zehn Knoten den Überblick verliert.
---
## Bipartites Matching: das Zuordnungsproblem {#sec:graphen-bipartites-matching-das-zuordnungsproblem}
## Bipartites Matching{idx:Bipartites Matching}: das Zuordnungsproblem {#sec:graphen-bipartites-matching-das-zuordnungsproblem}
Wenn $N$ Aufgaben auf $N$ Ressourcen **eins zu eins** verteilt werden — Orders auf Broker,
Schichten auf Mitarbeitende, Aufträge auf Maschinen — spricht man von **bipartitem
@ -319,9 +319,9 @@ $$
\sum_j x_{ij} = 1\ \forall i,\qquad \sum_i x_{ij} = 1\ \forall j,\qquad x_{ij}\ge0
$$
### Der Satz von Birkhoff und von Neumann
### Der Satz von Birkhoff und von Neumann{idx:Satz von Birkhoff und von Neumann}
> **Satz.** Die Extrempunkte der Menge aller doppelt-stochastischen Matrizen (alle
> **Satz.** Die Extrempunkte der Menge{idx:Konvexität!Menge} aller doppelt-stochastischen Matrizen (alle
> Zeilensummen $= 1$, alle Spaltensummen $= 1$, $x_{ij} \ge 0$) sind **genau** die
> Permutationsmatrizen (alle $x_{ij} \in \{0,1\}$).
@ -353,8 +353,8 @@ selbst“ ganzzahlig.
>
> **Und die Warnung dazu:** Diese Eigenschaft ist zerbrechlich. Eine einzige zusätzliche
> Nebenbedingung, die nicht in das Schema passt — „höchstens drei Fahrzeuge insgesamt“,
> eine Fixkostenkopplung, eine Mindestabnahmemenge — zerstört die totale Unimodularität.
> Dann liefert die Relaxation wieder Brüche, und Sie brauchen doch ein MILP. Prüfen Sie
> eine Fixkostenkopplung, eine Mindestabnahmemenge{idx:Mindestabnahmemenge} — zerstört die totale Unimodularität.
> Dann liefert die Relaxation{idx:Relaxation} wieder Brüche, und Sie brauchen doch ein MILP. Prüfen Sie
> das, bevor Sie sich auf die Struktur verlassen.
```python
@ -539,9 +539,9 @@ $$
> **💡 In der Praxis: nicht selbst modellieren**
> Die MTZ-Formulierung ist didaktisch wertvoll, aber für reale Instanzen zu schwach — die
> LP-Relaxation ist sehr locker, und Branch-and-Bound braucht sehr lange. Professionelle
> Solver verwenden stattdessen dynamisch erzeugte Subtour-Eliminationsschnitte oder, wie
> Solver verwenden stattdessen dynamisch erzeugte Subtour{idx:Subtour}-Eliminationsschnitte oder, wie
> OR-Tools, **spezialisierte Metaheuristiken**. Nutzen Sie für Routing die
> **Routing-Bibliothek**, nicht ein selbstgebautes MILP.
> **Routing-Bibliothek**{idx:Routing-Bibliothek}, nicht ein selbstgebautes MILP.
### Praxisbeispiel: Flotten-Routing
@ -722,7 +722,7 @@ if __name__ == "__main__":
> verschiebt sich der ganze Plan.
> * **Zu enge Zeitfenster ohne Vorabprüfung.** Ist ein Kunde in seinem Fenster physisch nicht
> erreichbar, meldet OR-Tools nur „keine Lösung“ — ohne zu sagen, welcher Kunde schuld ist.
> Die Funktion `pruefe_daten()` fängt genau das ab.
> Die Funktion{idx:Konvexität!Funktion} `pruefe_daten()` fängt genau das ab.
> * **Ergebnisse als exakt betrachten.** Die Routing-Bibliothek nutzt **Metaheuristiken**.
> Ein längeres Zeitlimit kann eine bessere Lösung liefern; „optimal“ wird hier in der Regel
> nicht bewiesen. Für die Praxis genügt das fast immer — man sollte es aber wissen.
@ -1032,7 +1032,7 @@ plausibel aussieht.
> * **Die Prüfung aus denselben Bausteinen bauen wie das Modell.** Wer die Ladung mit
> `loesung.Value(ladungs_dimension.CumulVar(...))` prüft, fragt das Modell, ob es sich an
> sich selbst hält. Rechnen Sie stattdessen aus der **ausgegebenen Tour** neu nach.
> * **Zwei Läufe nur an der Zielfunktion vergleichen.** 326 gegen 572 km sagt nichts,
> * **Zwei Läufe nur an der Zielfunktion{idx:Zielfunktion} vergleichen.** 326 gegen 572 km sagt nichts,
> solange nicht feststeht, dass beide Pläne überhaupt fahrbar sind.
> * **Unbenutzte Fahrzeuge übersehen.** Drei Fahrzeuge, die im Depot stehen, während eines
> alles fährt, sind fast immer ein Zeichen für eine fehlende Beschränkung.
@ -1086,7 +1086,7 @@ plausibel aussieht.
* **Graphen** sind die natürliche Sprache für Transport-, Zuordnungs- und Routenprobleme.
* **Flusserhaltung** ist die Kirchhoff-Regel des Operations Research: Was hineingeht, kommt
heraus — abzüglich des Knotensaldos.
* **Totale Unimodularität** macht Fluss- und Zuordnungsprobleme „von selbst“ ganzzahlig. Wer
* **Totale Unimodularität**{idx:Totale Unimodularität} macht Fluss- und Zuordnungsprobleme „von selbst“ ganzzahlig. Wer
hier `integrality` setzt, verschenkt Laufzeit ohne Gegenwert.
* **Spezialisierte Algorithmen schlagen allgemeine Solver** deutlich: Der Ungarische
Algorithmus{idx:Ungarischer Algorithmus} löst in $O(n^3)$, wofür ein MILP-Solver Branch-and-Bound bräuchte.

View file

@ -7,10 +7,10 @@
> und eine Entscheidung muss trotzdem heute fallen.
>
> **Voraussetzungen:** {ref:kap:milp} und {ref:kap:cpsat}, insbesondere
> {ref:sec:milp-gap} (MIP-Gap und Schranke).
> {ref:sec:milp-gap} (MIP-Gap{idx:MIP-Gap} und Schranke).
>
> **Danach können Sie:** eine lokale Suche mit ihrer entscheidenden Zutat — der
> Kostenänderung in O(1) — selbst schreiben, die Temperatur eines Simulated Annealing
> Kostenänderung in O(1) — selbst schreiben, die Temperatur eines Simulated Annealing{idx:Simulated Annealing}
> ausmessen statt raten, den Umschlagpunkt zwischen exakt und heuristisch für Ihr eigenes
> Problem bestimmen, und mit Large Neighborhood Search beides kombinieren.
>
@ -99,7 +99,7 @@ Nach diesem Kapitel können Sie …
1. … erklären, warum eine lokale Suche ohne **Kostenänderung in O(1)** praktisch wertlos ist.
2. … Simulated Annealing implementieren und seine Starttemperatur **an der Zuggröße**
kalibrieren statt am Zielfunktionswert.
3. … den Beitrag der Metaheuristik gegen die einfachere Alternative abgrenzen — reines
3. … den Beitrag der Metaheuristik{idx:Metaheuristik} gegen die einfachere Alternative abgrenzen — reines
Bergsteigen — statt ihn ihr gutzuschreiben.
4. … den **Umschlagpunkt** bestimmen, ab dem eine Metaheuristik den exakten Solver schlägt,
und begründen, warum diese Zahl problemspezifisch ist.
@ -136,7 +136,7 @@ $$
Wer die Aufgabe als Graph liest, erkennt sie wieder: Es ist ein **asymmetrisches
Rundreiseproblem** ({ref:kap:graphen}) mit offenem Ende. Das ist eine gute und eine
schlechte Nachricht. Gut, weil damit alles bekannt ist, was man über das Problem wissen
kann. Schlecht, weil dazu gehört, dass es NP-schwer ist.
kann. Schlecht, weil dazu gehört, dass es NP-schwer{idx:NP-schwer} ist.
> **📖 Definition: Metaheuristik**{idx:Metaheuristik}
>
@ -151,7 +151,7 @@ kann. Schlecht, weil dazu gehört, dass es NP-schwer ist.
### Die Instanz
Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die Rüstzeit setzt sich aus zwei
Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die Rüstzeit{idx:Rüstzeit} setzt sich aus zwei
Teilen zusammen: einem unregelmäßigen Reinigungsaufwand zwischen den Produktfamilien und
einem Zuschlag für den Wechsel von Dunkel nach Hell.
@ -895,7 +895,7 @@ if __name__ == "__main__":
print("=" * 88)
```
**Erwartete Ausgabe** (die CP-SAT-Spalten sind hardwareabhängig, siehe Programmkopf):
**Erwartete Ausgabe** (die CP-SAT{idx:CP-SAT}-Spalten sind hardwareabhängig, siehe Programmkopf):
```
========================================================================================
@ -1428,7 +1428,7 @@ Starttemperatur würde eine Verschlechterung von genau 70 Minuten in 37 % der F
angenommen? Vergleichen Sie das mit den Temperaturen in der Tabelle.
**Aufgabe ⭐⭐ — Die teure Bewertung.**
Ersetzen Sie in `Simulated_Annealing.py` die Funktion `delta_verschieben` durch eine
Ersetzen Sie in `Simulated_Annealing.py` die Funktion{idx:Konvexität!Funktion} `delta_verschieben` durch eine
Variante, die den Zug ausführt und `gesamtruestzeit()` neu berechnet. Wie viele Züge schafft
das Programm dann in derselben Zeit, und wie verändert sich das Ergebnis bei gleichem
Zeitbudget?
@ -1510,7 +1510,7 @@ liegt es an der Strategie oder an der Zahl der Runden, die sie schafft?
* Eine **Faustregel** ist selten dumm, aber immer kurzsichtig: Sie bewertet einen Schritt
danach, was er kostet, nicht danach, was er übrig lässt.
* **Lokale Suche** steht und fällt mit der Kostenänderung in $O(1)$. Wer den Zug über die
volle Zielfunktion bewertet, misst am Ende seine Implementierung statt des Verfahrens.
volle Zielfunktion{idx:Zielfunktion} bewertet, misst am Ende seine Implementierung statt des Verfahrens.
* **Simulated Annealing** nimmt Verschlechterungen mit $e^{-\Delta/T}$ an. $T$ und $\Delta$
haben dieselbe Einheit — die Temperatur gehört an der **Zuggröße** kalibriert, nicht am
Zielfunktionswert. Die verbreitete Regel „20 bis 50 % Annahmequote“ war auf dieser Aufgabe

View file

@ -73,7 +73,7 @@ je Stück-und-Rolle, und es kennt gar keine einzelnen Rollen mehr. Damit verschw
Problem, das das naheliegende Modell praktisch unlösbar macht — dazu gleich mehr.
> **🎯 Merksatz**
> Die schwierigste Arbeit an einem Optimierungsmodell ist selten die Zielfunktion. Es ist die
> Die schwierigste Arbeit an einem Optimierungsmodell ist selten die Zielfunktion{idx:Zielfunktion}. Es ist die
> Frage, **worüber** die Variablen laufen.
**Warum funktioniert das?** Weil hier alle 16 möglichen Muster aufzählbar waren. Bei drei
@ -98,7 +98,7 @@ Nach diesem Kapitel können Sie …
## Warum das naheliegende Modell scheitert {#sec:dekomposition-symmetrie}
Der erste Entwurf eines Zuschnittmodells sieht fast immer so aus: eine Binärvariable
Der erste Entwurf eines Zuschnittmodells sieht fast immer so aus: eine Binärvariable{idx:Binärvariable}
$z_{jr}$ für „Stück $j$ liegt auf Rolle $r$", dazu $y_r$ für „Rolle $r$ wird benutzt".
Zwei Dinge gehen dabei schief.
@ -108,14 +108,14 @@ Binärvariablen — unangenehm, aber allein noch kein Hindernis.
**Die Symmetrie.** Das ist das eigentliche Problem. Alle Mutterrollen sind **gleich**. Jede
Lösung existiert deshalb in unzähligen Umbenennungen: Vertauscht man Rolle 3 und Rolle 47,
entsteht eine formal andere, inhaltlich identische Lösung. Branch-and-Bound weiß das nicht
entsteht eine formal andere, inhaltlich identische Lösung. Branch-and-Bound{idx:Branch-and-Bound} weiß das nicht
und arbeitet sie einzeln ab.
> **⚠️ Woran man ein Symmetrieproblem erkennt**
>
> Der Suchbaum wächst, aber die **Schranke bewegt sich nicht.** Im Protokoll aus
> {ref:sec:milp-gap} sieht das so aus: Die Zahl der Knoten steigt in die Hunderttausende,
> der Incumbent verbessert sich hin und wieder, und der Dual Bound steht praktisch still.
> der Incumbent{idx:Branch-and-Bound!Incumbent} verbessert sich hin und wieder, und der Dual Bound steht praktisch still.
>
> Das ist ein anderes Bild als „das Problem ist einfach zu groß". Bei einem großen, aber
> unsymmetrischen Problem nähern sich beide Werte einander an, nur langsam.
@ -134,7 +134,7 @@ $$
>
> | Mathematik | Alltagssprache |
> | --- | --- |
> | $P$ | die Menge aller zulässigen Schnittmuster |
> | $P$ | die Menge{idx:Konvexität!Menge} aller zulässigen Schnittmuster |
> | $a_{ip}$ | „Wie viele Stücke der Breite $i$ liefert Muster $p$?" |
> | $x_p$ | „Wie oft schneide ich Muster $p$?" — die einzige Entscheidung |
> | $d_i$ | die bestellte Stückzahl der Breite $i$ |
@ -156,7 +156,7 @@ Die Idee ist alt (Gilmore und Gomory, 1961) und in einem Satz gesagt:
> Muster gibt, das sich lohnen würde — und wenn ja, nimm nur dieses eine dazu.
Die Frage „lohnt sich noch ein Muster?" beantworten die **Dualwerte** des Master-LPs. Der
Dualwert $\pi_i$ zur Bedarfszeile $i$ ist genau der Schattenpreis aus
Dualwert $\pi_i$ zur Bedarfszeile $i$ ist genau der Schattenpreis{idx:Schattenpreis} aus
{ref:sec:lp-dualitaet-und-schattenpreise}: Er sagt, wie viele Rollen ein zusätzliches Stück
der Breite $i$ kostet.
@ -189,7 +189,7 @@ $$
> Mustern optimal — auch über den nie erzeugten.
**Und wie findet man das beste neue Muster?** Man sucht die Zusammenstellung von Stücken mit
dem größten Gesamtwert, die noch in die Rolle passt. Das ist ein **Rucksackproblem**:
dem größten Gesamtwert, die noch in die Rolle passt. Das ist ein **Rucksackproblem**{idx:Rucksackproblem}:
| Rucksack ({ref:kap:milp}) | hier |
| --- | --- |

View file

@ -10,7 +10,7 @@ ist: Welches nehme ich, und was bekomme ich dafür?
| --- | --- | --- | --- | --- |
| **LP** | teilbare Mengen, lineare Zusammenhänge | beweisbares Optimum **plus Schattenpreise** | sobald etwas ganzzahlig sein muss | {ref:kap:lp} |
| **MILP** | Ja/Nein-Entscheidungen, Fixkosten, Logik | beweisbares Optimum, Gap als Fortschrittsmaß | Laufzeit wächst mit der Zahl der Binärvariablen | {ref:kap:milp} |
| **CP-SAT** | Zuweisung, Reihenfolge, Kalender, harte Logik | beweisbares Optimum, sehr ausdrucksstarke Bedingungen | keine Schattenpreise, keine stetigen Größen | {ref:kap:cpsat} |
| **CP-SAT**{idx:CP-SAT} | Zuweisung, Reihenfolge, Kalender, harte Logik | beweisbares Optimum, sehr ausdrucksstarke Bedingungen | keine Schattenpreise, keine stetigen Größen | {ref:kap:cpsat} |
| **Graphenalgorithmen** | Flüsse, Zuordnung, Touren | oft **polynomiell** statt exponentiell | nur, wenn die Struktur wirklich passt | {ref:kap:graphen} |
| **Metaheuristiken** | wenn der exakte Solver aussteigt | eine gute Lösung in **fester Zeit** | keine Garantie, kein Beweis, keine Schranke | {ref:kap:metaheuristiken} |
| **Spaltengenerierung** | Modelle mit astronomisch vielen Variablen | beweisbares Optimum über nie erzeugte Spalten | lohnt nur bei bestimmter Instanzstruktur | {ref:kap:dekomposition} |
@ -23,7 +23,7 @@ wissen, wo es aufhört — nicht, wofür es gedacht ist.
| Behauptung | Gemessen | Wo |
| --- | --- | --- |
| „Der exakte Solver ist immer besser." | Bei 500 Aufträgen liefert CP-SAT eine Lösung, die **5,2 % schlechter** ist als die Faustregel eines Meisters | {ref:kap:metaheuristiken} |
| „Metaheuristiken sind ungenau." | Faustregel 2 497 → Annealing 2 343 → Annealing plus LNS **2 289** Minuten Rüstzeit; untere Schranke 1 768 | {ref:kap:metaheuristiken} |
| „Metaheuristiken sind ungenau." | Faustregel 2 497 → Annealing 2 343 → Annealing plus LNS **2 289** Minuten Rüstzeit{idx:Rüstzeit}; untere Schranke 1 768 | {ref:kap:metaheuristiken} |
| „Man muss alle Variablen aufschreiben." | 28 erzeugte Muster von **46 408** möglichen — und die Lösung ist beweisbar optimal | {ref:kap:dekomposition} |
| „Dekomposition lohnt sich immer." | Bei drei Stücken je Rolle **11 %** Ersparnis, bei sechs **nichts** | {ref:kap:dekomposition} |
| „Mit Seed ist der Lauf reproduzierbar." | Schon **zwei** Arbeiter liefern bei identischem Seed drei verschiedene Pläne zum selben Zielwert | {ref:sec:cpsat-parallele-suche} |
@ -34,7 +34,7 @@ wissen, wo es aufhört — nicht, wofür es gedacht ist.
weit danebenliegen, und gerundet sogar unzulässig werden.
2. **Ein zu großes Big-M.** Es macht das Modell nicht falsch, sondern die Suche langsam und
die Schranken wertlos. So klein wie zulässig, nie „sicherheitshalber groß".
3. **Zu früh heuristisch werden.** Der Umschlagpunkt, ab dem eine Metaheuristik den exakten
3. **Zu früh heuristisch werden.** Der Umschlagpunkt, ab dem eine Metaheuristik{idx:Metaheuristik} den exakten
Solver schlägt, ist eine Eigenschaft **des Problems** und lässt sich messen. Wer ihn
nicht misst, verzichtet auf Optimalitätsgarantien, die er hätte haben können.

View file

@ -12,7 +12,7 @@ brauchen, hängt davon ab, **welche** davon bei Ihnen vorliegt:
> **🎯 Merksatz zum Diagramm**
> Die erste Frage trennt zwei Welten, die man nicht vermischen darf. Ist die
> **Nichtlinearität** das Problem, geht es um Konvexität — und damit darum, ob Sie eine
> **Nichtlinearität** das Problem, geht es um Konvexität{idx:Konvexität} — und damit darum, ob Sie eine
> Optimalitätsgarantie bekommen oder nur ein lokales Optimum. Ist die **Unsicherheit** das
> Problem, geht es um etwas ganz anderes: Nicht der Solver ist die Schwierigkeit, sondern
> die Frage, was „optimal“ überhaupt heißen soll, wenn man die Daten nicht kennt.
@ -33,15 +33,15 @@ insgesamt leistet, fasst die Synthese an seinem Ende zusammen.
> **📌 Kapitel auf einen Blick**
>
> **Worum geht es?** Risiko ist quadratisch. Sobald Varianz ins Spiel kommt, verlässt man
> die Welt der Polyeder. Dieses Kapitel liefert das mathematische Fundament für die gesamte
> die Welt der Polyeder{idx:Polyeder}. Dieses Kapitel liefert das mathematische Fundament für die gesamte
> Portfoliooptimierung in {ref:teil:anwendungen}.
>
> **Voraussetzungen:** {ref:kap:fundament} (Konvexität, Eigenwerte), {ref:kap:lp} (Dualität,
> Schattenpreise). Gradienten werden in {ref:sec:qp-nlp-lernziele} wiederholt.
>
> **Danach können Sie:** Ein quadratisches Programm aufstellen, die KKT-Bedingungen
> anwenden, den Zusammenhang zwischen Lagrange-Multiplikator und Schattenpreis erklären,
> eine gültige Kovarianzmatrix konstruieren — und mit nicht-konvexen Problemen umgehen,
> anwenden, den Zusammenhang zwischen Lagrange-Multiplikator{idx:Lagrange-Multiplikator} und Schattenpreis erklären,
> eine gültige Kovarianzmatrix{idx:Kovarianzmatrix} konstruieren — und mit nicht-konvexen Problemen umgehen,
> ohne ein lokales Optimum für das Optimum zu halten.
>
> **Zeitbedarf:** ca. 6,5 Stunden.
@ -120,8 +120,8 @@ $$
Der letzte Term ist der entscheidende: Bei negativer Korrelation ist er **negativ** und
zieht das Gesamtrisiko herunter. Deshalb verlässt man mit Risiko die Welt der Polyeder aus
{ref:teil:kernverfahren} — die Zielfunktion ist kein Skalarprodukt mehr, sondern eine
quadratische Form. Was das für die Lösbarkeit bedeutet und warum CVXPY das trotzdem mit
{ref:teil:kernverfahren} — die Zielfunktion{idx:Zielfunktion} ist kein Skalarprodukt{idx:Skalarprodukt} mehr, sondern eine
quadratische Form. Was das für die Lösbarkeit bedeutet und warum CVXPY{idx:CVXPY} das trotzdem mit
Optimalitätsgarantie löst, klärt der Rest des Kapitels.
> **🎯 Merksatz**
@ -139,7 +139,7 @@ Nach diesem Kapitel können Sie …
1. … ein **quadratisches Programm (QP)** aufstellen und von einem allgemeinen nichtlinearen
Programm abgrenzen.
2. … die **KKT-Bedingungen** herleiten und auf ein Optimierungsproblem anwenden.
3. … den Zusammenhang zwischen **Lagrange-Multiplikator** und **Schattenpreis** erklären.
3. … den Zusammenhang zwischen **Lagrange-Multiplikator** und **Schattenpreis**{idx:Schattenpreis} erklären.
4. … zwischen **konvex**{idx:Konvexität} und **streng konvex**{idx:Konvexität!streng konvex}
unterscheiden und eine gültige, positiv semidefinite **Kovarianzmatrix**{idx:Kovarianzmatrix}
konstruieren.
@ -204,11 +204,11 @@ Minimum.“* Korrekt gilt:
| Voraussetzung | Bedeutung | Folgerung |
| --- | --- | --- |
| $\mathbf{P} \succeq 0$ — **positiv semidefinit**, alle Eigenwerte $\lambda_i \ge 0$ | Die Funktion ist eine „Schüssel“, die eine flache Rinne haben darf | Problem ist **konvex**. Jedes lokale Minimum ist global. Es kann aber **mehrere Minimalstellen** geben — mit demselben Zielwert |
| $\mathbf{P} \succeq 0$ — **positiv semidefinit**, alle Eigenwerte $\lambda_i \ge 0$ | Die Funktion{idx:Konvexität!Funktion} ist eine „Schüssel“, die eine flache Rinne haben darf | Problem ist **konvex**. Jedes lokale Minimum ist global. Es kann aber **mehrere Minimalstellen** geben — mit demselben Zielwert |
| $\mathbf{P} \succ 0$ — **positiv definit**, alle $\lambda_i > 0$ | Echte Schüssel ohne flache Richtung | Problem ist **streng konvex**. Die Minimalstelle ist **eindeutig** |
| $\mathbf{P}$ hat einen negativen Eigenwert | Sattel oder Rinne nach unten | Problem ist **nicht konvex**. Lokale Optima möglich, keine Garantie |
**Warum das praktisch zählt:** Eine Stichproben-Kovarianzmatrix mit mehr Titeln als
**Warum das praktisch zählt:** Eine Stichproben-Kovarianzmatrix{idx:Stichproben-Kovarianzmatrix} mit mehr Titeln als
Beobachtungen ($N > T$) ist **singulär** — also nur semidefinit. Dann gibt es unendlich viele
Portfolios mit exakt demselben minimalen Risiko, und der Solver liefert eines davon,
scheinbar willkürlich. Kleine Datenänderungen führen zu völlig anderen Gewichten. Genau
@ -547,7 +547,7 @@ if __name__ == "__main__":
## Nichtlineare Optimierung mit `scipy.optimize.minimize` {#sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize}
Wenn Zielfunktion oder Nebenbedingungen weder linear noch quadratisch sind — Logarithmen,
Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie **SLSQP**
Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie **SLSQP**{idx:SLSQP}
(*Sequential Least Squares Programming*).
> **⚠️ Der entscheidende Unterschied zu CVXPY**
@ -564,7 +564,7 @@ Wir optimieren ein Portfolio nicht nur nach Varianz, sondern maximieren zusätzl
$$
\min_{\mathbf{w}}\quad \underbrace{\tfrac12\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}_{\text{Risiko}}
\;-\;\underbrace{\alpha\,\boldsymbol{\mu}^\top\mathbf{w}}_{\text{Ertrag}}
\;-\;\underbrace{\beta\Big(-\textstyle\sum_i w_i\ln w_i\Big)}_{\text{Diversifikation}}
\;-\;\underbrace{\beta\Big(-\textstyle\sum_i w_i\ln w_i\Big)}_{\text{Diversifikation{idx:Diversifikation}}}
$$
> **📐 Formel-Lesehilfe zur Entropie**
@ -575,7 +575,7 @@ $$
> **Ohne Formel gesagt:** Die Entropie misst, wie „breit verteilt“ das Portfolio ist. Weil
> sie mit einem Minuszeichen in die zu minimierende Zielfunktion eingeht, wird breite
> Streuung **belohnt** — und zwar mathematisch glatt, ohne harte Obergrenzen. Der Vorteil
> gegenüber einer Schranke wie $w_i \le 0{,}2$: Der Übergang ist weich, das Ergebnis
> gegenüber einer Schranke wie $w_i \le 0{,}2$: Der Übergang ist weich{idx:Nebenbedingung!weich}, das Ergebnis
> reagiert nicht sprunghaft auf kleine Datenänderungen.
```python
@ -789,7 +789,7 @@ unverändert gelassen. **Der weiche Term verteilt, die harte Schranke kappt.**
---
## Jenseits der Konvexität: lokale Optima, Multistart und MINLP {#sec:qp-nlp-nichtkonvex}
## Jenseits der Konvexität: lokale Optima, Multistart und MINLP{idx:MINLP} {#sec:qp-nlp-nichtkonvex}
Alles bisher in diesem Kapitel stand unter einer Bedingung: **Konvexität**. Sie ist der
Grund, warum CVXPY eine Optimalitätsgarantie geben kann und warum die KKT-Bedingungen nicht
@ -807,7 +807,7 @@ Effekten:
| --- | --- | --- |
| **Mengenrabatte** | Stückpreis fällt mit der Bestellmenge | Kostenfunktion wird konkav — Sprünge nach unten belohnen große Lose |
| **Skaleneffekte** | Stückkosten sinken mit der Losgröße | dasselbe Muster in der Produktion |
| **Produkte von Variablen** | „Menge mal Preis“, wenn beide entschieden werden | $x \cdot y$ ist weder konvex noch konkav |
| **Produkte von Variablen** | „Menge{idx:Konvexität!Menge} mal Preis“, wenn beide entschieden werden | $x \cdot y$ ist weder konvex noch konkav |
| **Ja/Nein mal Menge** | Anlage läuft *und* wie stark | ganzzahlig + nichtlinear = MINLP |
| **Verhältnisse** | Auslastungsgrad, Rendite je eingesetztem Euro | Quotienten sind selten konvex |
@ -1124,9 +1124,9 @@ schwierigste der in diesem Buch behandelten Klassen. Dafür gibt es eigene Werkz
| Werkzeug | Art | Was es leistet |
| --- | --- | --- |
| **Ipopt** (über `cyipopt` oder Pyomo) | lokaler NLP-Solver | Innere-Punkte-Verfahren für große, glatte NLPs mit tausenden Variablen — deutlich leistungsfähiger als `scipy`, aber ebenfalls **nur lokal** |
| **Ipopt** (über `cyipopt` oder Pyomo{idx:Pyomo}) | lokaler NLP-Solver | Innere-Punkte-Verfahren für große, glatte NLPs mit tausenden Variablen — deutlich leistungsfähiger als `scipy`, aber ebenfalls **nur lokal** |
| **Bonmin**, **Couenne** | MINLP | Bonmin lokal, Couenne mit globaler Garantie für viele Klassen |
| **SCIP**, **BARON**, **Gurobi** | global | Beweisbar globales Optimum, mit entsprechendem Rechenaufwand |
| **SCIP**{idx:SCIP}, **BARON**, **Gurobi**{idx:Gurobi} | global | Beweisbar globales Optimum, mit entsprechendem Rechenaufwand |
| **Pyomo** ({ref:sec:oekosystem-pyomo-linopy}) | Modellierungsschicht | Bindet alle oben genannten an, ohne dass das Modell umgeschrieben werden muss |
> **⚠️ Zu diesem Abschnitt gehört kein lauffähiges Programm.** Ipopt, Bonmin und Couenne
@ -1200,7 +1200,7 @@ korrelieren?)
**Aufgabe ⭐⭐⭐ — Effekt der Gewichtung untersuchen.**
Variieren Sie in `Entropie_Maximierte_Allokation.py` systematisch $\alpha \in
\{0{,}1;\ 0{,}5;\ 1;\ 5\}$ und $\beta \in \{0;\ 0{,}005;\ 0{,}015;\ 0{,}05\}$. Stellen Sie
für jede Kombination Rendite, Volatilität und Entropie in einer Tabelle dar.
für jede Kombination Rendite, Volatilität{idx:Volatilität} und Entropie in einer Tabelle dar.
(a) Wie verändert $\beta$ die Konzentration im Krypto-Titel?
(b) Ab welchem $\beta$ nähert sich die Lösung der Gleichgewichtung?
(c) Was ist Ihre Empfehlung — und mit welcher Begründung würden Sie sie einem
@ -1349,7 +1349,7 @@ groß ist der Unterschied zwischen bestem und schlechtestem Ergebnis? Kann die
Nachkommastelle. „Bester Wert aus 200 Startpunkten, Spanne bis …“ ist belastbar, ein
einzelner Wert ist es nicht.
**Ausblick.** {ref:kap:unsicherheit} gibt die Annahme auf, dass die Parameter überhaupt bekannt sind. Wir
**Ausblick.** {ref:kap:unsicherheit} gibt die Annahme auf, dass die Parameter{idx:Parameter (Modell)} überhaupt bekannt sind. Wir
lernen, mit Szenarien, Erwartungswerten und Worst Cases umzugehen.
---

View file

@ -2,7 +2,7 @@
> **📌 Kapitel auf einen Blick**
>
> **Worum geht es?** Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage,
> **Worum geht es?** Bisher waren alle Parameter{idx:Parameter (Modell)} bekannt. In der Realität sind Nachfrage,
> Rendite und Fahrzeit **Zufallsgrößen**. Dieses Kapitel zeigt vier Wege, damit umzugehen —
> und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist.
>
@ -114,8 +114,8 @@ Nach diesem Kapitel können Sie …
1. … den **Fluch des Durchschnitts**{idx:Fluch des Durchschnitts} (*Flaw of Averages*) an einem Beispiel erklären.
2. … ein **zweistufiges stochastisches Programm** mit Recourse formulieren.
3. … Monte-Carlo einsetzen, um Verteilungen statt Punktschätzungen zu bewerten.
4. … ein **robustes** Modell mit Unsicherheitsmenge aufstellen und seinen Preis beziffern.
5. … eine **Wahrscheinlichkeitszusage** („mit 95 % Sicherheit“) als Chance Constraint
4. … ein **robustes** Modell mit Unsicherheitsmenge{idx:Unsicherheitsmenge} aufstellen und seinen Preis beziffern.
5. … eine **Wahrscheinlichkeitszusage** („mit 95 % Sicherheit“) als Chance Constraint{idx:Chance Constraint}
schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis
eines Prozentpunkts Sicherheit beziffern.
6. … begründen, welcher der vier Ansätze für ein gegebenes Problem passt.
@ -274,9 +274,9 @@ Erwartungswert der Kosten ist größer als die Kosten des Erwartungswerts.
![Ansätze für Optimierung unter Unsicherheit](bilder_04/kap09_unsicherheit_ansaetze.svg)
| | **Monte-Carlo** | **Stochastische Programmierung** | **Robuste Optimierung** | **Chance Constraints** |
| | **Monte-Carlo** | **Stochastische Programmierung**{idx:Stochastische Programmierung} | **Robuste Optimierung**{idx:Robuste Optimierung} | **Chance Constraints** |
| --- | --- | --- | --- | --- |
| Braucht | Verteilung | Szenarien **mit Wahrscheinlichkeiten** | nur eine **Menge** möglicher Werte | Verteilung **oder** Szenarien |
| Braucht | Verteilung | Szenarien **mit Wahrscheinlichkeiten** | nur eine **Menge**{idx:Konvexität!Menge} möglicher Werte | Verteilung **oder** Szenarien |
| Optimiert | nichts (bewertet nur) | **Erwartungswert** über Szenarien | **Worst Case** in der Menge | Kosten bei **zugesicherter Quote** |
| Typische Frage | „Wie riskant ist dieser Plan?“ | „Was ist im Mittel am besten?“ | „Was hält auch im schlimmsten Fall?“ | „Was hält in 95 % der Fälle?“ |
| Stärke | beliebige Kennzahlen, sehr flexibel | nutzt Wahrscheinlichkeiten voll aus | keine Verteilungsannahme nötig | die Zusage steht **im Modell** |
@ -290,7 +290,7 @@ macht damit verhandelbar, was die Sicherheit kosten darf.
---
## Monte-Carlo-Simulation {#sec:unsicherheit-monte-carlo-simulation}
## Monte-Carlo-Simulation{idx:Monte-Carlo-Simulation} {#sec:unsicherheit-monte-carlo-simulation}
**Monte-Carlo**{idx:Monte-Carlo-Simulation} heißt schlicht: Ziehe sehr viele Zufallsszenarien, werte für jedes den Plan
aus, und betrachte die **Verteilung** der Ergebnisse statt eines einzigen Werts.
@ -662,7 +662,7 @@ Prognosen tatsächlich — anders als in dem Beispiel aus der Aufgabe *EVPI inte
---
## Robuste Optimierung: gegen den Worst Case absichern {#sec:unsicherheit-robuste-optimierung-gegen-den-worst-case}
## Robuste Optimierung{idx:Robuste Optimierung}: gegen den Worst Case absichern {#sec:unsicherheit-robuste-optimierung-gegen-den-worst-case}
Wenn Wahrscheinlichkeiten unbekannt oder instabil sind — Marktcrashs, Lieferkettenabrisse,
Pandemien —, hilft die **robuste Optimierung**{idx:Robuste Optimierung}. Sie fragt nicht nach dem Mittel, sondern
@ -856,19 +856,19 @@ z_{1-\alpha} = \Phi^{-1}(1-\alpha)
$$
Das ist eine **Second-Order-Cone-Bedingung**{idx:Konische Optimierung (SOCP, SDP)} — dieselbe
Bauform, die {ref:kap:qp-nlp} eingeführt hat, und CVXPY löst sie ohne Umstände.
Bauform, die {ref:kap:qp-nlp} eingeführt hat, und CVXPY{idx:CVXPY} löst sie ohne Umstände.
> **🎯 Warum eine Norm und keine Summe**
> Der Sicherheitszuschlag $\lVert\mathbf{L}^\top\mathbf{x}\rVert_2$ ist **keine** Summe von
> Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige
> Quellen schwanken **gemeinsam** weniger als jede für sich, und die Norm rechnet das
> automatisch mit. Ein fester Aufschlag je Anlage könnte das nicht — er wäre linear und
> würde Diversifikation nicht belohnen.
> würde Diversifikation{idx:Diversifikation} nicht belohnen.
### Weg 2: Szenarien — Big-M ohne Verteilungsannahme
Liegen statt einer Verteilung nur $S$ beobachtete Szenarien vor, bekommt jedes eine
Binärvariable $z_s$, die sagt, ob es verletzt werden **darf**:
Binärvariable{idx:Binärvariable} $z_s$, die sagt, ob es verletzt werden **darf**:
$$
\begin{aligned}
@ -1203,11 +1203,11 @@ Verteilung.** Der Plan verspricht 95 % und hält **87,4 %**. Nicht weil das Verf
rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil
die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht **alles
zugleich** ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine
Kovarianzmatrix mit Korrelationen um null kann so etwas nicht ausdrücken.
Kovarianzmatrix{idx:Kovarianzmatrix} mit Korrelationen um null kann so etwas nicht ausdrücken.
Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er kommt auf
94,8 %, kostet dafür 16,7 % mehr — und kauft **keine Windkraft mehr**. Die Logik dahinter ist
hart und richtig: Was im Ernstfall ausfällt, hilft der Zusage nicht.
hart{idx:Nebenbedingung!hart} und richtig: Was im Ernstfall ausfällt, hilft der Zusage nicht.
> **⚠️ Die Szenariomethode überanpasst — und mehr Szenarien helfen nicht verlässlich**
> Bei $S$ Szenarien und $\alpha = 5\,\%$ darf das Modell genau $0{,}05 \cdot S$ davon
@ -1336,7 +1336,7 @@ das lässt sich in CVXPY mit `cp.sum_largest` formulieren.)
> {ref:sec:unsicherheit-schnellstart} führt der Mittelwert in die Irre, weil die **Kosten**
> asymmetrisch sind. Hier führt er in die Irre, weil die **Verknüpfung** asymmetrisch ist:
> Ein Gewerk, das früher fertig wird, hilft niemandem — ein Gewerk, das sich verspätet,
> verzögert alles. Beide Male gilt: *Der Mittelwert einer Funktion ist nicht die Funktion
> verzögert alles. Beide Male gilt: *Der Mittelwert einer Funktion{idx:Konvexität!Funktion} ist nicht die Funktion
> des Mittelwerts.*
---
@ -1368,7 +1368,7 @@ das lässt sich in CVXPY mit `cp.sum_largest` formulieren.)
> und rechnet deterministisch. Wann ist das unproblematisch?**
> (a) Immer — der Erwartungswert ist die beste Einzelschätzung.
> (b) Nie.
> (c) Wenn Zielfunktion und Nebenbedingungen in den unsicheren Größen **linear** sind und
> (c) Wenn Zielfunktion{idx:Zielfunktion} und Nebenbedingungen in den unsicheren Größen **linear** sind und
> keine Entscheidung erst nach Beobachtung fällt. Sobald Minimum, Maximum, Betrag oder eine
> Nachbesserungsentscheidung im Spiel ist, gilt es nicht mehr.
@ -1421,7 +1421,7 @@ das lässt sich in CVXPY mit `cp.sum_largest` formulieren.)
Entscheidung nicht begründbar.
**Ausblick.** {ref:kap:dynamische-programmierung} fügt die Zeitdimension hinzu: Entscheidungen, die über viele
Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung.
Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung{idx:Bellman-Gleichung}.
---

View file

@ -1,4 +1,4 @@
# Kapitel: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution {#kap:dynamische-programmierung}
# Kapitel: Dynamische Programmierung — Die Bellman-Gleichung{idx:Bellman-Gleichung} und Order-Execution {#kap:dynamische-programmierung}
> **📌 Kapitel auf einen Blick**
>
@ -8,7 +8,7 @@
> **Voraussetzungen:** {ref:kap:unsicherheit} (Erwartungswerte). Rekursion in Python.
>
> **Danach können Sie:** Ein mehrperiodiges Problem in Zustände und Stufen zerlegen, die
> Wertfunktion durch Rückwärtsinduktion berechnen, die optimale Strategie ableiten — und
> Wertfunktion durch Rückwärtsinduktion{idx:Rückwärtsinduktion} berechnen, die optimale Strategie ableiten — und
> prüfen, ob Ihr Zustand vollständig genug ist.
>
> **Zeitbedarf:** ca. 5 Stunden.
@ -85,7 +85,7 @@ zusätzlichen bezahlt.
**Warum funktioniert das?** Weil man den Rest des Weges nicht neu durchdenken muss, sobald
man einmal an einem Ort steht: Wie man dorthin gekommen ist, ist für die Zukunft egal. Diese
Eigenschaft heißt **Bellmansches Optimalitätsprinzip** und ist der Grund, warum aus einem
Eigenschaft heißt **Bellmansches Optimalitätsprinzip**{idx:Optimalitätsprinzip} und ist der Grund, warum aus einem
Problem mit exponentiell vielen Wegen eine Rechnung mit wenigen Zeilen wird. Bei fünf Orten
fällt das kaum auf — bei fünfzig ist es der Unterschied zwischen Sekunden und Jahren.
@ -99,10 +99,10 @@ Nach diesem Kapitel können Sie …
2. … Zustand, Stufe, Aktion und Wertfunktion für ein gegebenes Problem benennen.
3. … eine Rückwärtsinduktion{idx:Rückwärtsinduktion} von Hand für ein kleines Problem durchführen.
4. … das Almgren-Chriss-Ausführungsproblem lösen und die Lösung interpretieren.
5. … einschätzen, wann DP funktioniert und wann der „Fluch der Dimensionalität“ zuschlägt.
5. … einschätzen, wann DP funktioniert und wann der „Fluch der Dimensionalität{idx:Fluch der Dimensionalität}“ zuschlägt.
6. … die Wertfunktion als **Nachschlagetabelle** lesen: Sie liefert keine Planfolge, sondern
eine Regel, die auch bei Abweichungen gilt.
7. … einen Zustandsraum auf Vollständigkeit prüfen und erkennen, wann eine Kostenart
7. … einen Zustandsraum{idx:Zustandsraum} auf Vollständigkeit prüfen und erkennen, wann eine Kostenart
Vorgeschichte erfordert.
---
@ -328,13 +328,13 @@ C(n_t, X_t) = \underbrace{\eta\,n_t^2}_{\text{Marktauswirkung}} \;+\; \underbrac
$$
> **📐 Formel-Lesehilfe**
> * $n_t$ — in Periode $t$ verkaufte Stückzahl. Quadriert, weil doppelte Menge mehr als
> * $n_t$ — in Periode $t$ verkaufte Stückzahl. Quadriert, weil doppelte Menge{idx:Konvexität!Menge} mehr als
> doppelten Preisdruck erzeugt.
> * $\eta$ („eta“) — Slippage-Koeffizient. Wie stark reagiert der Markt auf Volumen?
> * $X_t$ — Restbestand **nach** dem Verkauf. Quadriert, weil Varianz quadratisch mit der
> Positionsgröße wächst.
> * $\lambda$ — Risikoaversion: Wie sehr stört mich Schwankung im Vergleich zu Slippage?
> * $\sigma$ — Volatilität je Periode.
> * $\sigma$ — Volatilität{idx:Volatilität} je Periode.
>
> **Ohne Formel gesagt:** „Schnell verkaufen kostet Preisabschlag. Langsam verkaufen kostet
> Nervenkitzel. Finde die Mitte.“
@ -352,7 +352,7 @@ $$
> holding_risk = risk_aversion * (sigma_period ** 2) * (remaining ** 2) * 1e5
> ```
> Woher eine solche Zahl kommt, bleibt dann unklar. Faktisch wirkt sie als versteckte
> Erhöhung der Risikoaversion um fünf Größenordnungen — ein Parameter wie
> Erhöhung der Risikoaversion um fünf Größenordnungen — ein Parameter{idx:Parameter (Modell)} wie
> `risk_aversion = 1e-6` würde dann in Wahrheit $0{,}1$ bedeuten. Solche „magischen
> Zahlen“ machen ein Modell unkalibrierbar: Niemand kann sagen, ob 1e-6 viel oder wenig
> ist.
@ -693,7 +693,7 @@ Zustandsvariable **multipliziert** den Aufwand.
**Gegenmittel:**
* **Zustandsraum**{idx:Zustandsraum} **verkleinern:** gröber diskretisieren, irrelevante Variablen weglassen.
* **Approximate Dynamic Programming**{idx:Dynamische Programmierung!Approximate Dynamic Programming}**:** $V_t$ durch eine parametrische Funktion annähern
* **Approximate Dynamic Programming**{idx:Dynamische Programmierung!Approximate Dynamic Programming}**:** $V_t$ durch eine parametrische Funktion{idx:Konvexität!Funktion} annähern
statt tabellarisch zu speichern.
* **Reinforcement Learning**{idx:Reinforcement Learning}**:** dieselbe Bellman-Gleichung{idx:Bellman-Gleichung}, aber $V$ wird aus Erfahrung
gelernt (Q-Learning{idx:Q-Learning}) statt vollständig berechnet.
@ -720,7 +720,7 @@ nicht nur vom aktuellen Zustand)?
Wertfunktionstabelle und bestimmen Sie den optimalen Pfad. Prüfen Sie mit
`Bellman_Minimalbeispiel.py` (angepasst).
**Aufgabe ⭐⭐ — Rucksackproblem als DP.**
**Aufgabe ⭐⭐ — Rucksackproblem{idx:Rucksackproblem} als DP.**
Lösen Sie das Rucksackproblem aus {ref:kap:milp} mit dynamischer Programmierung statt MILP.
(Zustand: verbleibende Kapazität; Stufe: betrachteter Gegenstand.) Vergleichen Sie
Laufzeit und Ergebnis mit dem MILP-Solver.

View file

@ -10,7 +10,7 @@
> {ref:kap:milp}. Für den Denkfehler hilft {ref:kap:markowitz}.
>
> **Danach können Sie:** begründen, warum eine gewichtete Summe bei ganzzahligen Modellen
> Kompromisse **grundsätzlich** nicht erreichen kann; eine vollständige Pareto-Front mit dem
> Kompromisse **grundsätzlich** nicht erreichen kann; eine vollständige Pareto-Front{idx:Pareto-Front} mit dem
> ε-Constraint-Verfahren berechnen; und einer Geschäftsführung eine Entscheidung vorlegen,
> die sie tatsächlich treffen kann.
>
@ -128,7 +128,7 @@ $$
> **📖 Definition: Pareto-Front**{idx:Pareto-Front}
>
> Die Menge aller nicht dominierten Lösungen. Sie ist das vollständige Ergebnis eines
> Die Menge{idx:Konvexität!Menge} aller nicht dominierten Lösungen. Sie ist das vollständige Ergebnis eines
> Mehrzielproblems — nicht ein Punkt, sondern eine Liste.
>
> Wer daraus einen einzelnen Punkt macht, trifft eine Entscheidung. Die Frage ist nur, ob er
@ -645,7 +645,7 @@ Eine Front mit zehn Punkten ist kein Ergebnis, sondern eine Speisekarte. Damit s
entscheidbar wird, gehört eine Spalte dazu, die im Programm `EUR je kg` heißt:
$$
\text{Schattenpreis} = \frac{\text{Aufpreis gegenüber dem Kostenminimum}}
\text{Schattenpreis{idx:Schattenpreis}} = \frac{\text{Aufpreis gegenüber dem Kostenminimum}}
{\text{eingesparte Kilogramm}}
$$
@ -657,7 +657,7 @@ kaufmännisch schon getroffen.
>
> | Stelle | Was sie tut |
> | --- | --- |
> | `plane(ziel, co2_grenze, kosten_grenze)` | **eine** Funktion für alle Varianten. Welches Ziel und welche Schranke — mehr Unterschied gibt es zwischen den fünf Teilen des Programms nicht |
> | `plane(ziel, co2_grenze, kosten_grenze)` | **eine** Funktion{idx:Konvexität!Funktion} für alle Varianten. Welches Ziel und welche Schranke — mehr Unterschied gibt es zwischen den fünf Teilen des Programms nicht |
> | `pareto_front()` | das ε-Verfahren: Grenze jeweils um 1 kg unter den zuletzt erreichten Wert drücken, bis nichts mehr geht |
> | `untere_huelle()` | rechnet die Hülle **unabhängig** aus, damit die Aussage „unerreichbar“ nicht auf dem Gewichtsraster beruht, sondern auf Geometrie |
>
@ -683,7 +683,7 @@ würden Sie empfehlen — und wie begründen Sie ihn in einem Satz?
Erhöhen Sie `BAHN_TRASSEN` von 5 auf 8. Was passiert mit der Länge der Front und mit der
Zahl der unerreichbaren Punkte? Erklären Sie den Zusammenhang.
**Aufgabe ⭐⭐ — Die Front der Relaxation.**
**Aufgabe ⭐⭐ — Die Front der Relaxation{idx:Relaxation}.**
Lassen Sie `integrality` weg, sodass Anteile erlaubt sind. Berechnen Sie die Front erneut
(mit einem ε-Raster, da es nun unendlich viele Punkte gibt). Wie viele Punkte liegen jetzt
oberhalb der konvexen Hülle — und warum?
@ -771,4 +771,4 @@ einer Fläche. Wie ändert sich das ε-Verfahren, und warum wächst der Aufwand
---
*Weiter mit:* [{ref:kap:prognose} — Predict-then-Optimize](34_Predict_then_Optimize.md)
*Weiter mit:* [{ref:kap:prognose} — Predict-then-Optimize{idx:Predict-then-Optimize}](34_Predict_then_Optimize.md)

View file

@ -1,4 +1,4 @@
# Kapitel: Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung {#kap:prognose}
# Kapitel: Predict-then-Optimize{idx:Predict-then-Optimize} — die bessere Prognose, die schlechtere Entscheidung {#kap:prognose}
> **📌 Kapitel auf einen Blick**
>
@ -6,7 +6,7 @@
> Ein Modell **prognostiziert**, ein anderes **entscheidet**. Beide arbeiten sauber — und
> genau an der Naht entsteht ein Fehler, den keine der beiden Seiten sieht.
>
> **Voraussetzungen:** {ref:kap:unsicherheit}, insbesondere das Newsvendor-Problem und das
> **Voraussetzungen:** {ref:kap:unsicherheit}, insbesondere das Newsvendor-Problem{idx:Newsvendor-Problem} und das
> kritische Verhältnis. Etwas Regression hilft, ist aber nicht nötig.
>
> **Danach können Sie:** begründen, warum ein Prognosemodell mit kleinerem MSE teurere
@ -61,7 +61,7 @@ derselben Daten, aus denen auch das Quantil kommt. Trotzdem kostet er **7,20 €
gut 10 %.
Der Grund steht in den Preisen: Ein fehlendes Brot kostet doppelt so viel wie ein übriges.
Bei asymmetrischen Kosten liegt die optimale Menge nicht in der Mitte der Verteilung,
Bei asymmetrischen Kosten liegt die optimale Menge{idx:Konvexität!Menge} nicht in der Mitte der Verteilung,
sondern beim **kritischen Verhältnis** $c_-/(c_- + c_+) = 6/9 = 66{,}7\,\%$ — das kennen Sie
aus {ref:kap:unsicherheit}.
@ -578,7 +578,7 @@ Die dritte Zeile der Ergebnistabelle — „Zuschlag auf Kosten trainiert" — i
denkbare Form einer Idee, die derzeit erforscht wird: **das Prognosemodell direkt auf die
Entscheidungskosten zu trainieren** statt auf ein Fehlermaß.
Hier war es ein einziger Parameter, mit einer Rasterschleife bestimmt. Der allgemeine Fall
Hier war es ein einziger Parameter{idx:Parameter (Modell)}, mit einer Rasterschleife bestimmt. Der allgemeine Fall
ist schwieriger, weil die Optimierung zwischen Modell und Kosten steht: Um den Gradienten
der Kosten nach den Modellparametern zu bilden, muss man **durch das Optimierungsproblem
hindurch ableiten**.
@ -631,7 +631,7 @@ und zwar **in unterschiedlichem Ausmaß**? Was sagt das über den Wert eines Mer
>
> Ein Data-Science-Team stellt sein Quartalsergebnis vor:
>
> > *„Wir haben das alte lineare Modell durch ein Gradient-Boosting-Modell ersetzt. Der MSE
> > *„Wir haben das alte lineare Modell durch ein Gradient{idx:Gradient}-Boosting-Modell ersetzt. Der MSE
> > auf den Testdaten ist um 18 % gesunken, der MAPE um 14 %. Das ist die größte
> > Prognoseverbesserung, die wir je erreicht haben. Die Disposition bekommt ab nächstem
> > Monat die neuen Werte."*

View file

@ -24,7 +24,7 @@ Er hat drei Abschnitte, und Sie können nach jedem aufhören:
> ersten beiden Kapitel dieses Teils und kann den Rest überspringen, ohne dass etwas
> fehlt — die Methoden sind dieselben.
>
> **Lesen Sie ihn trotzdem, wenn Sie eines der folgenden Themen brauchen:** Schätzfehler in
> **Lesen Sie ihn trotzdem, wenn Sie eines der folgenden Themen brauchen:** Schätzfehler{idx:Schätzfehler} in
> Kovarianzmatrizen (das Problem tritt bei jeder Korrelationsschätzung auf, auch bei
> Lieferzeiten), Risikomaße jenseits der Standardabweichung, oder rollierende Auswertung
> mit sauberer Trennung von Trainings- und Testzeitraum.
@ -114,7 +114,7 @@ Der interessanteste Teil steht in der zweiten Zeile der Ausgabe. **60,26 € je
Risikobudget** — das ist der Dualwert derselben Nebenbedingung, die in der Werkstatt sagt,
was eine zusätzliche Montagestunde wert wäre. In der Finanzwelt hat diese Zahl einen eigenen
Namen: der **Preis des Risikos**. Es ist nicht bloß eine Analogie, es ist derselbe
Schattenpreis aus {ref:kap:lp}.
Schattenpreis{idx:Schattenpreis} aus {ref:kap:lp}.
> **🎯 Merksatz**
> Wenn zwei Probleme dieselbe Struktur haben, brauchen sie nicht zwei Modelle, sondern zwei
@ -122,7 +122,7 @@ Schattenpreis aus {ref:kap:lp}.
> — nicht daran, dass eine Tabelle es behauptet.
**Warum funktioniert das?** Weil ein LP nichts über die Bedeutung seiner Zahlen weiß. Es
sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor. Ob in der Matrix Stunden,
sieht eine Matrix, einen Kapazitätsvektor{idx:Kapazitätsvektor} und einen Zielvektor. Ob in der Matrix Stunden,
Kilogramm oder Risikobeiträge stehen, kommt darin nicht vor.
---
@ -646,7 +646,7 @@ Gebühr um 10 Einheiten zu erhöhen. Bis zu welcher Gebühr lohnt sich das — u
Einschränkung dieser Aussage kennen Sie aus {ref:sec:lp-entartung}?
**Aufgabe ⭐⭐ — Die Brücke rückwärts.**
Übertragen Sie die Ledoit-Wolf-Shrinkage aus {ref:kap:finanzdaten} auf Lieferzeiten:
Übertragen Sie die Ledoit-Wolf-Shrinkage{idx:Ledoit-Wolf-Shrinkage} aus {ref:kap:finanzdaten} auf Lieferzeiten:
Wo genau träte dort dasselbe Problem auf, und welche Daten bräuchten Sie?
**Aufgabe ⭐⭐ — CVaR mit Ganzzahligkeit.**
@ -670,7 +670,7 @@ trägt — und schreiben Sie auf, welche der drei Zeilen im Weg steht.
> geht vor wie im Portfoliomanagement:
>
> > *„Ich habe für jeden Lieferanten aus den letzten drei Jahren die mittlere Lieferzeit und
> > die Kovarianzmatrix der Lieferzeiten geschätzt. Dann habe ich das Markowitz-Modell
> > die Kovarianzmatrix{idx:Kovarianzmatrix} der Lieferzeiten geschätzt. Dann habe ich das Markowitz-Modell
> > angewendet: minimiere die Varianz der Gesamtlieferzeit bei vorgegebener mittlerer
> > Lieferzeit. Das Ergebnis ist ein sauber diversifiziertes Lieferantenportfolio."*
>
@ -723,7 +723,7 @@ trägt — und schreiben Sie auf, welche der drei Zeilen im Weg steht.
## Zusammenfassung {#sec:bruecke-zusammenfassung}
* Die Behauptung „dieselben Methoden, andere Domäne" ist prüfbar — und hier geprüft: Ein
Depot passt unverändert in die Klasse `Produktionsproblem`, und **eine** CVaR-Funktion
Depot passt unverändert in die Klasse `Produktionsproblem`, und **eine** CVaR-Funktion{idx:Konvexität!Funktion}
bearbeitet Kursverluste wie Lieferverzüge.
* Der **Schattenpreis** einer Ressource heißt in der Finanzwelt *Preis des Risikos*. Es ist
derselbe Dualwert derselben Nebenbedingung.

View file

@ -125,7 +125,7 @@ hinaus:
| **1** | Welcher Block läuft in welcher Stunde? | **binär**, für alle Szenarien gleich | am Vorabend, bevor der Wind bekannt ist |
| **2** | Wie viel liefert jeder laufende Block? | kontinuierlich, je Szenario verschieden | am Tag selbst, laufend anpassbar |
Im Kapitel Unsicherheit war die erste Stufe kontinuierlich (Kapazität kaufen, Menge
Im Kapitel Unsicherheit war die erste Stufe kontinuierlich (Kapazität kaufen, Menge{idx:Konvexität!Menge}
festlegen). Hier ist sie **ganzzahlig** — und das ändert die Sache grundlegend: Eine
Kapazität lässt sich anteilig erhöhen, ein Kraftwerk nicht zu 30 % anfahren.
@ -710,7 +710,7 @@ Ergänzen Sie einen Batteriespeicher (100 MW, 400 MWh, Wirkungsgrad 90 %). Um wi
die erwarteten Kosten — und was ist der Speicher damit je MWh Kapazität wert?
**Aufgabe ⭐⭐⭐ — Wenn es größer wird.**
Erhöhen Sie auf 20 Blöcke und 168 Stunden (eine Woche). Beobachten Sie MIP-Gap und
Erhöhen Sie auf 20 Blöcke und 168 Stunden (eine Woche). Beobachten Sie MIP-Gap{idx:MIP-Gap} und
Rechenzeit ({ref:sec:milp-gap}). Ab wann wird das Problem für den exakten Solver zu groß —
und welches Verfahren aus {ref:kap:metaheuristiken} würden Sie einsetzen?

View file

@ -10,9 +10,9 @@ die Kapitel danach, **welche** Voraussetzung bei Ihnen verletzt ist.
| --- | --- | --- | --- |
| **Linearität** | Risiko wächst quadratisch, Kosten degressiv, Sättigung | QP, konvexe Optimierung, KKT | {ref:kap:qp-nlp} |
| **Feste Daten** | Nachfrage, Wind, Rendite stehen erst morgen fest | Monte-Carlo, Zweistufigkeit, Robustheit, Chance Constraints | {ref:kap:unsicherheit} |
| **Eine Periode** | die Entscheidung heute verändert, was übermorgen möglich ist | Bellman-Gleichung, Rückwärtsinduktion | {ref:kap:dynamische-programmierung} |
| **Ein Ziel** | zwei Kennzahlen sollen gleichzeitig stimmen | Pareto-Front, ε-Constraint | {ref:kap:mehrziel} |
| **Daten sind gegeben** | die Eingabe ist selbst eine Prognose | Predict-then-Optimize, entscheidungsorientiertes Lernen | {ref:kap:prognose} |
| **Eine Periode** | die Entscheidung heute verändert, was übermorgen möglich ist | Bellman-Gleichung{idx:Bellman-Gleichung}, Rückwärtsinduktion{idx:Rückwärtsinduktion} | {ref:kap:dynamische-programmierung} |
| **Ein Ziel** | zwei Kennzahlen sollen gleichzeitig stimmen | Pareto-Front{idx:Pareto-Front}, ε-Constraint | {ref:kap:mehrziel} |
| **Daten sind gegeben** | die Eingabe ist selbst eine Prognose | Predict-then-Optimize{idx:Predict-then-Optimize}, entscheidungsorientiertes Lernen | {ref:kap:prognose} |
Die letzte Zeile ist die unbequemste: Dort ist nicht das Modell unsicher, sondern seine
**Eingabe** — und die kommt aus einem zweiten Modell, das nach anderen Kriterien gebaut
@ -32,7 +32,7 @@ wurde.
1. **Unsicherheit durch Mittelwerte ersetzen.** Der Durchschnittskunde kauft nie. Bei
asymmetrischen Kosten liegt das Optimum systematisch neben dem Mittelwert, und eine
Terminzusage ist ein **Quantil**, kein Erwartungswert.
2. **Konvexität voraussetzen, ohne sie zu prüfen.** Ohne sie gibt es keine
2. **Konvexität{idx:Konvexität} voraussetzen, ohne sie zu prüfen.** Ohne sie gibt es keine
Optimalitätsgarantie, sondern nur ein lokales Ergebnis — und der Solver sagt das nicht
von selbst.
3. **Zwei Ziele mit einem Gewicht verrechnen, bevor die Front bekannt ist.** Ein Gewicht
@ -43,7 +43,7 @@ wurde.
> **🎯** Jede der vier Annahmen lässt sich einzeln aufgeben — aber jede kostet etwas
> Bestimmtes: Linearität kostet die Optimalitätsgarantie, feste Daten kosten Rechenzeit
> oder Vorsicht, eine Periode kostet Zustandsraum, ein Ziel kostet eine Entscheidung, die
> oder Vorsicht, eine Periode kostet Zustandsraum{idx:Zustandsraum}, ein Ziel kostet eine Entscheidung, die
> nicht der Modellierer treffen darf. Wer weiß, **welche** Annahme bei ihm fällt, weiß
> auch, welchen Preis er zahlt.

View file

@ -10,8 +10,8 @@
> Statistik.
>
> **Danach können Sie:** Kursdaten korrekt laden, diskrete und logarithmische Renditen
> unterscheiden, das Schätzfehlerproblem erklären, Ledoit-Wolf-Shrinkage anwenden — und
> erkennen, wann eine Kovarianzmatrix aus zu wenigen Beobachtungen stammt.
> unterscheiden, das Schätzfehlerproblem erklären, Ledoit-Wolf-Shrinkage{idx:Ledoit-Wolf-Shrinkage} anwenden — und
> erkennen, wann eine Kovarianzmatrix{idx:Kovarianzmatrix} aus zu wenigen Beobachtungen stammt.
>
> **Zeitbedarf:** ca. 5 Stunden.
>
@ -73,7 +73,7 @@ $100 \cdot e^{0{,}1823} = 120{,}00$ — exakt der beobachtete Endkurs.
| Verknüpfung über die Zeit | multiplikativ: $\prod(1+r_t)$ | **additiv**: $\sum \ln(1+r_t)$ |
| Mitteln zulässig? | nein — führt systematisch zu hoch | ja |
| Verknüpfung über Anlagen | **additiv**: $\sum w_i r_i$ | nein (Summe von Logs ≠ Log der Summe) |
| Wofür also | Portfoliogewichtung, Kennzahlen je Periode | Zeitreihenanalyse, Volatilität, Modelle |
| Wofür also | Portfoliogewichtung, Kennzahlen je Periode | Zeitreihenanalyse, Volatilität{idx:Volatilität}, Modelle |
> **🎯 Merksatz**
> Beide Renditearten sind richtig — für **verschiedene Richtungen**. Diskrete Renditen
@ -94,9 +94,9 @@ Rest dieses Kapitels behandelt die zweite große Fehlerquelle bei Finanzdaten
Nach diesem Kapitel können Sie …
1. … erklären, wann man diskrete und wann logarithmische Renditen verwendet.
2. … beschreiben, warum die Stichproben-Kovarianzmatrix bei vielen Titeln unbrauchbar wird.
2. … beschreiben, warum die Stichproben-Kovarianzmatrix{idx:Stichproben-Kovarianzmatrix} bei vielen Titeln unbrauchbar wird.
3. … den **Error-Maximizer-Effekt**{idx:Error-Maximizer-Effekt} an einem Experiment nachweisen.
4. … Ledoit-Wolf-Shrinkage anwenden und ihre Wirkung an Eigenwerten und Konditionszahl
4. … Ledoit-Wolf-Shrinkage anwenden und ihre Wirkung an Eigenwerten und Konditionszahl{idx:Konditionszahl}
messen.
5. … Kursdaten so laden, dass die Spaltenreihenfolge garantiert stimmt.
6. … begründen, warum eine Schätzung aus $T < N$ Beobachtungen dem Optimierer risikofreie
@ -111,7 +111,7 @@ Sei $P_{i,t}$ der **bereinigte** Schlusskurs (*adjusted close*){idx:Adjusted Clo
$t$. „Bereinigt“ heißt: um Dividenden und Aktiensplits korrigiert — sonst erscheint jede
Dividendenzahlung als Kurssturz.
### Diskrete Rendite
### Diskrete Rendite{idx:Diskrete Rendite}
$$
R_{i,t} = \frac{P_{i,t} - P_{i,t-1}}{P_{i,t-1}} = \frac{P_{i,t}}{P_{i,t-1}} - 1
@ -123,7 +123,7 @@ $$
R_{p,t} = \sum_{i=1}^n w_i R_{i,t} = \mathbf{w}^\top\mathbf{R}_t
$$
### Logarithmische Rendite
### Logarithmische Rendite{idx:Logarithmische Rendite}
$$
r_{i,t} = \ln\!\left(\frac{P_{i,t}}{P_{i,t-1}}\right) = \ln P_{i,t} - \ln P_{i,t-1}
@ -234,7 +234,7 @@ if __name__ == "__main__":
---
## Das Schätzfehler-Problem {#sec:finanzdaten-das-schaetzfehler-problem}
## Das Schätzfehler{idx:Schätzfehler}-Problem {#sec:finanzdaten-das-schaetzfehler-problem}
Sei $\mathbf{X}\in\mathbb{R}^{T\times N}$ die Matrix zentrierter Renditen von $N$ Titeln über
$T$ Handelstage. Die **Stichproben-Kovarianzmatrix**{idx:Stichproben-Kovarianzmatrix} lautet:
@ -243,7 +243,7 @@ $$
\mathbf{S} = \frac{1}{T-1}\mathbf{X}^\top\mathbf{X}
$$
Das Problem: $\mathbf{S}$ hat $\frac{N(N+1)}{2}$ zu schätzende Parameter, aber nur
Das Problem: $\mathbf{S}$ hat $\frac{N(N+1)}{2}$ zu schätzende Parameter{idx:Parameter (Modell)}, aber nur
$T \cdot N$ Datenpunkte. Bei $N = 50$ Titeln sind das **1275 Parameter** — geschätzt aus
einem Jahr Daten ($T = 252$).

View file

@ -8,7 +8,7 @@
> **Voraussetzungen:** {ref:kap:qp-nlp} (QP, KKT), {ref:kap:finanzdaten} (Kovarianz, Shrinkage).
>
> **Danach können Sie:** Die Effizienzgrenze berechnen, das Maximum-Sharpe-Portfolio über
> die Korn-Transformation bestimmen, institutionelle Nebenbedingungen einbauen — und
> die Korn-Transformation{idx:Korn-Transformation} bestimmen, institutionelle Nebenbedingungen einbauen — und
> beurteilen, wann eine Renditeschätzung das Ergebnis wertlos macht.
>
> **Zeitbedarf:** ca. 6 Stunden.
@ -78,7 +78,7 @@ Lesen Sie die dritte Spalte: Sie zeigt, wie viel Rendite je Einheit Risiko herau
* Bei **4 %**: **0,75** — das beste Verhältnis.
* Bei 8 %: 0,44 — das renditestärkste Portfolio ist das **schlechteste** je Risikoeinheit.
Und es besteht zu 100 % aus Aktien: Wer die höchste Rendite fordert, bekommt zwangsläufig
gar keine Diversifikation mehr.
gar keine Diversifikation{idx:Diversifikation} mehr.
Von 3 % auf 8 % Rendite ist ein Zuwachs um Faktor 2,7 — das Risiko steigt dabei um Faktor
3,7. Die letzten Prozentpunkte Rendite sind die teuersten.
@ -90,7 +90,7 @@ Von 3 % auf 8 % Rendite ist ein Zuwachs um Faktor 2,7 — das Risiko steigt dabe
> sondern eine unternehmerische Frage.
**Warum funktioniert das?** Weil `cp.Parameter` das Modell nur **einmal** aufbaut und dann
sechsmal mit verschiedenen Zielwerten löst. Genau dafür gibt es Parameter in CVXPY: Bei 200
sechsmal mit verschiedenen Zielwerten löst. Genau dafür gibt es Parameter{idx:Parameter (Modell)} in CVXPY: Bei 200
Punkten auf der Linie spart das den 200-fachen Kompilierungsaufwand
({ref:sec:oekosystem-vektorisierung}). Der Rest dieses Kapitels führt die Effizienzlinie
sauber aus, zeigt, wie man das beste Verhältnis direkt berechnet statt es abzulesen — und
@ -315,7 +315,7 @@ $$
$$
> **📐 Formel-Lesehilfe**
> Zähler: **Überrendite** über den risikolosen Zins $r_f$. Nenner: die Volatilität.
> Zähler: **Überrendite** über den risikolosen Zins $r_f$. Nenner: die Volatilität{idx:Volatilität}.
>
> **Ohne Formel gesagt:** „Wie viel Mehrertrag bekomme ich je Einheit Risiko, die ich
> eingehe?“ Eine Sharpe Ratio von 0,8 heißt: Für jeden Prozentpunkt Volatilität gibt es
@ -323,7 +323,7 @@ $$
### Die Korn-Transformation
Die Sharpe Ratio ist ein **Bruch** — und damit **nicht konvex**. CVXPY würde sie ablehnen.
Die Sharpe Ratio ist ein **Bruch** — und damit **nicht konvex**. CVXPY{idx:CVXPY} würde sie ablehnen.
Der Ausweg ist ein eleganter Trick, der die Homogenität des Problems ausnutzt:
$$
@ -362,7 +362,7 @@ In der Praxis gelten regulatorische und interne Regeln:
| **Sektorlimit** | $\sum_{i\in\text{Tech}} w_i \le 0{,}35$ | Branchenkonzentration begrenzen |
| **Mindestposition** | $w_i = 0$ oder $w_i \ge w_{\min}$ | Streuverluste vermeiden |
| **Kardinalität** | $\sum_i z_i \le K$, $w_i \le M z_i$ | Verwaltungsaufwand begrenzen |
| **Turnover-Grenze** | $\|\mathbf{w}-\mathbf{w}_{\text{alt}}\|_1 \le \tau$ | Transaktionskosten dämpfen |
| **Turnover-Grenze** | $\|\mathbf{w}-\mathbf{w}_{\text{alt}}\|_1 \le \tau$ | Transaktionskosten{idx:Transaktionskosten} dämpfen |
### Was diese Regeln kosten
@ -703,7 +703,7 @@ if __name__ == "__main__":
> Das Programm vergleicht immer mit dem **1/N-Portfolio**. Das ist kein Scherz: DeMiguel,
> Garlappi und Uppal zeigten 2009 in einer viel beachteten Studie, dass die naive
> Gleichgewichtung viele optimierte Strategien out of sample schlägt — weil sie keinerlei
> Schätzung benötigt und damit auch keinen Schätzfehler enthält. **Wenn Ihre Optimierung
> Schätzung benötigt und damit auch keinen Schätzfehler{idx:Schätzfehler} enthält. **Wenn Ihre Optimierung
> 1/N nicht schlägt, ist sie ihren Aufwand nicht wert.** Diese Messlatte sollte in jedem
> Portfolioprojekt stehen.
@ -759,9 +759,9 @@ zweiten. Vergleichen Sie GMV, Max Sharpe und 1/N.
## Finde den Denkfehler {#sec:markowitz-denkfehler}
{ref:kap:finanzdaten} hat gezeigt, was eine schlecht geschätzte **Kovarianzmatrix**
{ref:kap:finanzdaten} hat gezeigt, was eine schlecht geschätzte **Kovarianzmatrix**{idx:Kovarianzmatrix}
anrichtet. Bei den erwarteten **Renditen** ist es schlimmer — und weil sie in der
Zielfunktion stehen statt in der Nebenbedingung, fällt es schwerer auf.
Zielfunktion{idx:Zielfunktion} stehen statt in der Nebenbedingung, fällt es schwerer auf.
> **🐛 Finde den Denkfehler: Zwölf gleiche Anlagen, ein sehr ungleiches Portfolio**
>

View file

@ -1,4 +1,4 @@
# Kapitel: Tail-Risiko, CVaR und Transaktionskosten {#kap:cvar}
# Kapitel: Tail-Risiko, CVaR und Transaktionskosten{idx:Transaktionskosten} {#kap:cvar}
> **📌 Kapitel auf einen Blick**
>
@ -86,12 +86,12 @@ Nach diesem Kapitel können Sie …
1. … erklären, warum Marktrenditen keine Normalverteilung haben und was daraus folgt.
2. … VaR und CVaR definieren und begründen, warum nur der CVaR kohärent ist.
3. … das Rockafellar-Uryasev-Theorem anwenden, um den CVaR linear zu formulieren.
3. … das Rockafellar-Uryasev-Theorem{idx:Rockafellar-Uryasev-Theorem} anwenden, um den CVaR linear zu formulieren.
4. … Transaktionskosten über die $L_1$-Norm modellieren.
5. … Einheiten konsistent halten und Nebenbedingungen vektorisieren.
6. … erklären, warum zwei Anlagen mit identischem VaR völlig verschiedene Extremverluste
haben können.
7. … die Folgen fehlender Subadditivität für die Verteilung von Risikobudgets benennen.
7. … die Folgen fehlender Subadditivität{idx:Subadditivität} für die Verteilung von Risikobudgets benennen.
---
@ -250,7 +250,7 @@ alle **zwei Jahre**. Der Faktor beträgt fast **eine Million**.
Und Teil 2 zeigt das Grundproblem des VaR an einem Beispiel, das Sie von Hand nachrechnen
können: Einzeln meldet er für jede Anleihe einen *Gewinn* von 2 €, im Portfolio einen
*Verlust* von 98 €. Diversifikation hätte demnach das Risiko um 102 € erhöht. Der CVaR
*Verlust* von 98 €. Diversifikation{idx:Diversifikation} hätte demnach das Risiko um 102 € erhöht. Der CVaR
dagegen verhält sich korrekt: 101,33 € im Portfolio gegenüber 159,24 € bei getrennter
Betrachtung — die Streuung **senkt** das Risiko, wie es sein muss.
@ -305,7 +305,7 @@ nicht überschritten wird.
| --- | --- | --- |
| Berücksichtigt Verlusthöhe im Rand | ✗ nein | ✓ ja |
| **Subadditiv**{idx:Subadditivität} ($\rho(A+B) \le \rho(A)+\rho(B)$) | ✗ nein | ✓ ja |
| Kohärentes Risikomaß | ✗ nein | ✓ ja |
| Kohärentes Risikomaß{idx:Kohärentes Risikomaß} | ✗ nein | ✓ ja |
| Konvex und optimierbar | ✗ nein | ✓ ja |
| Regulatorischer Standard | bis Basel II | **ab Basel III** |
@ -343,7 +343,7 @@ $$
> der Verlust darüber hinausgeht. Der CVaR ist die Schwelle plus der gemittelte Überschuss
> — und zwar für diejenige Schwelle, bei der diese Summe minimal wird.“
>
> **Warum $u_s$ automatisch das Maximum wird:** Die Zielfunktion minimiert die Summe der
> **Warum $u_s$ automatisch das Maximum wird:** Die Zielfunktion{idx:Zielfunktion} minimiert die Summe der
> $u_s$. Jedes $u_s$ wird also so klein wie möglich gedrückt — bis an die Grenze, die die
> beiden Ungleichungen erlauben. Das ist genau das Maximum der beiden Untergrenzen.
@ -367,8 +367,8 @@ $$
>
> > **⚠️ Die teuerste Verwechslung des Kapitels**
> > $\alpha$ und $1-\alpha$ zu vertauschen führt zu **keiner Fehlermeldung**. Das Modell
> > rechnet weiter, nur eben über die falsche Menge. An 2 000 simulierten Tagesrenditen mit
> > Fat Tails gemessen:
> > rechnet weiter, nur eben über die falsche Menge{idx:Konvexität!Menge}. An 2 000 simulierten Tagesrenditen mit
> > Fat Tails{idx:Fat Tails} gemessen:
> >
> > | Rechnung | gemittelt über | CVaR |
> > | --- | --- | --- |
@ -408,7 +408,7 @@ $$
> gegenüber vielen kleinen. Das entspricht genau dem, was man in der Praxis will — nicht 50
> Kleinstorders mit je 3 € Mindestgebühr.
In CVXPY schreibt man einfach `cp.norm1(w - w_alt)`; intern wird das in lineare
In CVXPY{idx:CVXPY} schreibt man einfach `cp.norm1(w - w_alt)`; intern wird das in lineare
Hilfsvariablen zerlegt.
---
@ -425,7 +425,7 @@ Hilfsvariablen zerlegt.
> ```
> Der Risikoterm ist dadurch faktisch um Faktor 252 zu schwach gewichtet:
> `lambda_risk = 1.5` bedeutet dann effektiv eine Risikoaversion von $1{,}5/252 \approx 0{,}006$.
> Das Modell ist nicht falsch im Sinne von unlösbar — aber der Parameter bedeutet nicht,
> Das Modell ist nicht falsch im Sinne von unlösbar — aber der Parameter{idx:Parameter (Modell)} bedeutet nicht,
> was er zu bedeuten scheint, und lässt sich daher nicht sinnvoll einstellen.
>
> **Dieses Programm rechnet deshalb durchgehend auf Tagesbasis** und annualisiert erst in
@ -655,7 +655,7 @@ if __name__ == "__main__":
> und **nicht** als das Naheliegende, `cp.sqrt(cp.quad_form(w, sigma))`. Der naheliegende
> Ausdruck ist nicht DCP und wird von CVXPY mit einem `DCPError` abgelehnt: `cp.sqrt` ist
> konkav und verlangt deshalb ein **konkaves** Argument, `quad_form` ist aber konvex. Die
> Regel wird verletzt, obwohl die Funktion als ganze mathematisch völlig harmlos ist —
> Regel wird verletzt, obwohl die Funktion{idx:Konvexität!Funktion} als ganze mathematisch völlig harmlos ist —
> $\sqrt{w^\top \Sigma w}$ ist konvex, DCP kann es nur nicht *sehen*.
>
> Wichtig ist die Fehlerdiagnose dahinter, weil sie leicht in die Irre geht:
@ -673,7 +673,7 @@ if __name__ == "__main__":
> **💡 Zur Annualisierung von VaR und CVaR**
> Ein häufiger Fehler skaliert den täglichen CVaR mit $\sqrt{252}$. Diese
> **Wurzel-Zeit-Regel** gilt streng nur für **Standardabweichungen** unabhängig identisch
> **Wurzel-Zeit-Regel**{idx:Wurzel-Zeit-Regel} gilt streng nur für **Standardabweichungen** unabhängig identisch
> verteilter Größen ohne Drift. Der CVaR ist ein Erwartungswert über einen Verteilungsrand
> — für ihn ist die Regel eine grobe Näherung, die bei fetten Rändern und Autokorrelation
> systematisch danebenliegt.
@ -711,7 +711,7 @@ Angenommen, ein Modell verrechnet Jahresrendite gegen Tages-CVaR (siehe die Warn
Wirkung wie `RISIKOAVERSION = 1.5` im konsistenten Tagesmodell zu erzielen?
**Aufgabe ⭐⭐⭐ — Risikoaversion kalibrieren.**
Variieren Sie `RISIKOAVERSION` von 0 bis 20 und tragen Sie Rendite, Volatilität, CVaR und
Variieren Sie `RISIKOAVERSION` von 0 bis 20 und tragen Sie Rendite, Volatilität{idx:Volatilität}, CVaR und
Turnover gegeneinander auf.
(a) Wie sieht die „Effizienzgrenze“ im Rendite-CVaR-Raum aus?
(b) Bei welchem Wert entspricht das CVaR-Portfolio ungefähr dem Varianz-Portfolio?
@ -822,7 +822,7 @@ $\tau \in \{0{,}05;\ 0{,}1;\ 0{,}25;\ 0{,}5;\ 1{,}0\}$.
> Antworten: [Anhang A](90_Anhang_Loesungen.md#selbsttest-loesung-cvar).
1. Was misst der CVaR, was der VaR nicht misst?
2. Warum ist der CVaR konvex, aber nicht streng konvex?
2. Warum ist der CVaR konvex, aber nicht streng konvex{idx:Konvexität!streng konvex}?
3. Welche Rolle spielt die Hilfsvariable $\gamma$ bei Rockafellar/Uryasev?
4. Warum bevorzugt die $L_1$-Strafe wenige große statt vieler kleiner Umschichtungen?
5. Warum sollte man tägliche und annualisierte Größen nicht in einer Zielfunktion mischen?
@ -852,7 +852,7 @@ $\tau \in \{0{,}05;\ 0{,}1;\ 0{,}25;\ 0{,}5;\ 1{,}0\}$.
Zeit.
**Ausblick.** {ref:kap:handelsmaschine} fügt alles zusammen: Datenpipeline, Signal, Optimierung,
Rebalancing und ein Walk-Forward-Backtest ohne Lookahead-Bias.
Rebalancing{idx:Rebalancing} und ein Walk-Forward-Backtest{idx:Walk-Forward-Backtest} ohne Lookahead-Bias.
---

View file

@ -3,11 +3,11 @@
> **📌 Kapitel auf einen Blick**
>
> **Worum geht es?** Um das Zusammenführen von allem: Datenpipeline, Signal, Optimierung,
> Rebalancing, Kostenverbuchung und ein Backtest, der nicht lügt.
> Rebalancing{idx:Rebalancing}, Kostenverbuchung und ein Backtest, der nicht lügt.
>
> **Voraussetzungen:** {ref:kap:finanzdaten} bis {ref:kap:cvar}.
>
> **Danach können Sie:** Eine Walk-Forward-Backtest-Architektur bauen, Lookahead-Bias
> **Danach können Sie:** Eine Walk-Forward-Backtest{idx:Walk-Forward-Backtest}-Architektur bauen, Lookahead-Bias{idx:Lookahead-Bias}
> vermeiden, Ergebnisse gegen eine Benchmark bewerten und typische Backtest-Fallen erkennen.
>
> **Zeitbedarf:** ca. 6 Stunden.
@ -93,7 +93,7 @@ Nach diesem Kapitel können Sie …
1. … die sechs Bausteine einer quantitativen Engine benennen und ihre Reihenfolge begründen.
2. … einen Walk-Forward-Backtest{idx:Walk-Forward-Backtest} ohne Lookahead-Bias{idx:Lookahead-Bias} implementieren.
3. … Rebalancing-Termine korrekt auf Handelstage abbilden.
4. … Transaktionskosten und Gewichtsdrift realistisch verbuchen.
4. … Transaktionskosten{idx:Transaktionskosten} und Gewichtsdrift realistisch verbuchen.
5. … Backtest-Ergebnisse kritisch prüfen und die typischen Selbsttäuschungen erkennen.
6. … den maximalen Rückschlag als eigenständige Kennzahl berichten und begründen, warum
Rendite und Sharpe allein nichts über den Verlauf aussagen.
@ -646,7 +646,7 @@ FEHLER 1+2+3 kombiniert 25.41 % +19.80 %
* **Lookahead** erzeugt eine scheinbare Überrendite von **15,4 Prozentpunkten pro Jahr**.
Ein einziger falscher Index — `iloc[t+1]` statt `iloc[t]` — verwandelt Rauschen in eine
scheinbar brillante Strategie.
* **Survivorship-Bias** liefert **13,1 Prozentpunkte** allein dadurch, dass man das
* **Survivorship-Bias**{idx:Survivorship-Bias} liefert **13,1 Prozentpunkte** allein dadurch, dass man das
Universum im Nachhinein auf die Gewinner beschränkt.
* Selbst die **ehrliche** Variante zeigt noch +1,1 Prozentpunkte — reines Rauschen, das
aber ohne Weiteres als „leichte Outperformance“ verkauft werden könnte.
@ -664,7 +664,7 @@ Die fünf Fallen im Einzelnen:
| 2 | **Survivorship-Bias**{idx:Survivorship-Bias} | Nur heute existierende Titel im Universum | Historische Indexzusammensetzung verwenden |
| 3 | **Kostenblindheit** | Gebühren, Spread, Slippage ignoriert | Kosten im Modell **und** in der Verbuchung |
| 4 | **Overfitting**{idx:Overfitting} | Parameter auf die Vergangenheit getrimmt | Out-of-Sample-Zeitraum strikt zurückhalten |
| 5 | **Data Snooping**{idx:Data Snooping} | Viele Varianten getestet, beste berichtet | Zahl der Versuche protokollieren; Deflated Sharpe Ratio |
| 5 | **Data Snooping**{idx:Data Snooping} | Viele Varianten getestet, beste berichtet | Zahl der Versuche protokollieren; Deflated Sharpe Ratio{idx:Sharpe-Ratio} |
> **🎯 Die unbequeme Wahrheit**
> Falle 5 ist die gefährlichste, weil sie sich nicht im Code zeigt, sondern im
@ -686,7 +686,7 @@ Backtest und echtem Einsatz liegt:
| **Papierhandel** | Funktioniert es auf Live-Daten ohne Geld? | 36 Monate, oft schlechter als der Backtest |
| **Kapazitätsanalyse** | Bewegt meine eigene Order den Markt? | Begrenzt das einsetzbare Volumen |
| **Regimewechsel-Test** | Hält es in Krisen? | Backtest über 2008, 2020 separat auswerten |
| **Betriebsrisiken** | Was, wenn der Datenfeed ausfällt? | Fallback-Strategie, Monitoring |
| **Betriebsrisiken** | Was, wenn der Datenfeed ausfällt? | Fallback-Strategie{idx:Fallback-Strategie}, Monitoring |
| **Regulatorik** | MiFID II, Best Execution, Dokumentation | Protokollpflichten |
> **⚠️ Der Realitätsabschlag**
@ -777,7 +777,7 @@ der beim ersten Versuch funktioniert hat.
Das folgende Programm macht den Effekt messbar. Es testet Strategien, die
**nachweislich** keinerlei Vorteil haben — ihre Tagesergebnisse sind Zufallszahlen mit
Erwartungswert exakt null — und zeigt, wie gut die jeweils beste davon aussieht, allein
als Funktion der Anzahl der Versuche.
als Funktion{idx:Konvexität!Funktion} der Anzahl der Versuche.
```python
#!/usr/bin/env python3

View file

@ -8,10 +8,10 @@ konkurrierende Verwendungen verteilen, unter Unsicherheit.**
| Werkstatt und Kraftwerk | Depot und Handel | Was mathematisch dahintersteht |
| --- | --- | --- |
| Produkt, Maschinenstunde | Titel, Kapital | Entscheidungsvariable |
| Produkt, Maschinenstunde | Titel, Kapital | Entscheidungsvariable{idx:Entscheidungsvariable} |
| Deckungsbeitrag | erwartete Rendite | linearer Zielterm |
| Kapazität, Liefervertrag | Positionsgrenze, Sektorlimit | Nebenbedingung |
| Rüstkosten, Anfahrkosten | Transaktionskosten | Fixkosten mit Binärvariable (B1) |
| Rüstkosten, Anfahrkosten | Transaktionskosten{idx:Transaktionskosten} | Fixkosten mit Binärvariable{idx:Binärvariable} (B1) |
| Lastabwurf, Konventionalstrafe | Tail-Verlust | Straf- bzw. Risikoterm |
| Wind- und Nachfrageszenarien | Renditeszenarien | Szenariomenge |
@ -26,12 +26,12 @@ sondern eine Redewendung.
| „Planen mit der Prognose ist gut genug." | Der Erwartungswert-Plan kostet **149 % mehr** (874 870 € statt 351 356 €) und wirft in **28 von 40** Szenarien Last ab | {ref:kap:supplychain} |
| „Dann sparen wir eben beim Brennstoff." | Genau das tut er: 12 725 € gespart, **536 239 €** Lastabwurf bezahlt — das 42-fache. Die Anfahrkosten sind in beiden Plänen gleich | {ref:kap:supplychain} |
| „Der VaR sagt, wie riskant es ist." | VaR **1,86 %**, CVaR **2,99 %** — und der schlechteste Tag der Stichprobe (**23,0 %**) ändert am VaR **nichts** | {ref:kap:cvar} |
| „Die Kovarianzmatrix ist eine Eingabe wie jede andere." | Aus kurzen Reihen geschätzt ist sie schlecht konditioniert; die Optimierung setzt dann auf Schätzrauschen | {ref:kap:finanzdaten} |
| „Die Kovarianzmatrix{idx:Kovarianzmatrix} ist eine Eingabe wie jede andere." | Aus kurzen Reihen geschätzt ist sie schlecht konditioniert; die Optimierung setzt dann auf Schätzrauschen | {ref:kap:finanzdaten} |
## Drei Fehler, die dieser Teil verhindert
1. **Bei Markowitz einsteigen.** Wer {ref:kap:finanzdaten} überspringt, optimiert seine
eigenen Schätzfehler und wundert sich über Gewichte, die niemand halten würde.
eigenen Schätzfehler{idx:Schätzfehler} und wundert sich über Gewichte, die niemand halten würde.
2. **Den VaR für ein Risikomaß halten.** Er sagt, **ob** die Schwelle gerissen wird, nicht
**wie schlimm** es dahinter aussieht — und er ist nicht kohärent. Optimiert wird der
CVaR.

View file

@ -68,7 +68,7 @@ gefunden:**
| Zielwert nachrechnen | Fixkosten, die im Modell nicht verbucht wurden | {ref:sec:milp-denkfehler} |
> **🎯 Merksatz**
> Entscheidend ist, was diese Funktion **nicht** benutzt: kein Solver-Objekt, keine
> Entscheidend ist, was diese Funktion{idx:Konvexität!Funktion} **nicht** benutzt: kein Solver-Objekt, keine
> Modellvariable, keine Dimension aus der Bibliothek. Sie bekommt nur die ausgegebene
> Lösung und die Anforderungen — und rechnet unabhängig nach. Eine Prüfung, die dieselben
> Bausteine verwendet wie das Modell, prüft das Modell gegen sich selbst und findet
@ -89,9 +89,9 @@ Nach diesem Kapitel können Sie …
1. … die fünf typischen Praxisfallen benennen und je ein Gegenmittel nennen.
2. … ein unlösbares Modell in einen „am wenigsten schlechten“ Plan verwandeln.
3. … Solver-Ergebnisse erklärbar machen (*Explainable OR*).
3. … Solver-Ergebnisse erklärbar machen (*Explainable OR*{idx:Explainable OR}).
4. … für jede Nebenbedingung beziffern, was sie den Plan kostet — und begründen, warum
der Schattenpreis diese Frage nur für die *nächste* Einheit beantwortet.
der Schattenpreis{idx:Schattenpreis} diese Frage nur für die *nächste* Einheit beantwortet.
5. … Zeitlimits und Optimalitätslücken sinnvoll setzen.
6. … die Architektur einer produktionsreifen OR-Plattform skizzieren.
7. … eine **modellunabhängige Abnahmeprüfung** schreiben, die eine Lösung gegen die
@ -109,14 +109,14 @@ Nach diesem Kapitel können Sie …
![Die fünf typischen Praxisfallen](bilder_04/kap15_praxisfallen.svg)
### Falle 1 — Infeasibility
### Falle 1 — Infeasibility{idx:Infeasibility}
Wenn Parameter unglücklich zusammentreffen — drei Mitarbeitende gleichzeitig krank, kein
qualifizierter Ersatz —, meldet der Solver schlicht `INFEASIBLE`.
* **Falsch:** Das System stürzt ab oder zeigt „Fehler“. Der Disponent steht ohne Plan da.
* **Richtig:** **Hierarchische Relaxation.**{idx:Hierarchische Relaxation} Jede harte Bedingung, die im Notfall gebrochen
werden darf, erhält eine teure Schlupfvariable. So liefert der Solver immer einen Plan —
werden darf, erhält eine teure Schlupfvariable{idx:Schlupfvariable}. So liefert der Solver immer einen Plan —
und zeigt zugleich präzise, **wo** es klemmt.
```python
@ -380,30 +380,30 @@ Derselbe Unterschied kehrt auf Modellebene wieder, und dort lässt er sich in Eu
beziffern: {ref:sec:praxisfallen-attribution} misst für jede Bedingung, was sie den
ganzen Plan kostet — und baut daraus einen Bericht, den man vorlegen kann.
### Falle 3 — Regimewechsel und Schätzfehler
### Falle 3 — Regimewechsel und Schätzfehler{idx:Schätzfehler}
Ein Modell, das auf einer mehrjährigen Aufwärtsphase kalibriert wurde, kennt keine
Liquiditätskrise und keine Zinswende.
* **Robuste Schätzung** statt roher Historie: Ledoit-Wolf-Shrinkage ({ref:kap:finanzdaten}),
* **Robuste Schätzung** statt roher Historie: Ledoit-Wolf-Shrinkage{idx:Ledoit-Wolf-Shrinkage} ({ref:kap:finanzdaten}),
Szenarien ({ref:kap:unsicherheit}).
* **Harte Obergrenzen** für Einzelpositionen und Sektoren — auch dann, wenn der Solver
rechnerisch alles in einen Titel legen möchte. Die Grenze kostet Ertrag im Normalfall und
rettet im Ernstfall.
* **Rollierende Neuschätzung** statt einmaliger Kalibrierung.
### Falle 4 — Lookahead- und Survivorship-Bias
### Falle 4 — Lookahead- und Survivorship-Bias{idx:Survivorship-Bias}
Ausführlich in {ref:kap:handelsmaschine} behandelt. Die Kurzfassung: Beide Fehler machen Backtests
systematisch zu gut, ohne eine Fehlermeldung zu erzeugen. Der Lookahead-Selbsttest aus
{ref:sec:handelsmaschine-die-engine} findet den ersten automatisch.
### Falle 5 — Laufzeitexplosion
### Falle 5 — Laufzeitexplosion{idx:Laufzeitexplosion (Fehlerbild)}
Exakte Optimalität kann bei MILP oder CP-SAT Minuten bis Stunden dauern — und die Laufzeit
Exakte Optimalität kann bei MILP oder CP-SAT{idx:CP-SAT} Minuten bis Stunden dauern — und die Laufzeit
wächst nicht linear mit der Problemgröße.
| Einsatzszenario | Empfohlenes Zeitlimit | Akzeptabler MIP-Gap |
| Einsatzszenario | Empfohlenes Zeitlimit | Akzeptabler MIP-Gap{idx:MIP-Gap} |
| --- | --- | --- |
| Interaktive Oberfläche | 510 Sekunden | 25 % |
| Batch tagsüber | 15 Minuten | 12 % |
@ -422,9 +422,9 @@ wächst nicht linear mit der Problemgröße.
---
## Constraint Attribution: welche Bedingung kostet wie viel? {#sec:praxisfallen-attribution}
## Constraint Attribution{idx:Constraint Attribution}: welche Bedingung kostet wie viel? {#sec:praxisfallen-attribution}
Der Constraint-Trace beantwortet „warum **diese** Zuweisung?“. Der Deletion Filter in
Der Constraint-Trace beantwortet „warum **diese** Zuweisung?“. Der Deletion Filter{idx:Deletion Filter} in
{ref:anhang:fehlerdiagnose} beantwortet „warum geht es **gar nicht**?“. Der häufigste Fall
in der Praxis liegt dazwischen, und für ihn hat bisher niemand ein Werkzeug bekommen:
@ -880,7 +880,7 @@ weg, ist er erfüllbar, keine allein ist „der“ Grund. Genau die Struktur, di
angewandt.
Die Pointe steht am Ende: Mit +15 Stunden Lackierkapazität wird der Wunsch erfüllbar — und
kostet dann **nichts**. Der Plan wählt diese Menge von sich aus, sobald der Engpass weg ist.
kostet dann **nichts**. Der Plan wählt diese Menge{idx:Konvexität!Menge} von sich aus, sobald der Engpass weg ist.
Der Konflikt bestand nie zwischen Vertrieb und Produktion. Er bestand zwischen dem Vertrieb
und der Lackiererei, und niemand wusste das.
@ -929,7 +929,7 @@ In einer professionellen Umgebung ist der Optimierer kein Skript, sondern ein
| --- | --- | --- |
| **Quellsysteme** | ERP, Personalplanung, Marktdaten-Feeds | REST, Datenbanken, Broker-APIs |
| **Datenqualität** | Validierung, Plausibilität, unveränderlicher Snapshot | Pydantic, Pandera, Great Expectations |
| **Optimierungs-Worker** | Modell bauen, lösen, Timeout, Fallback | Celery/Redis, OR-Tools, HiGHS, CVXPY |
| **Optimierungs-Worker** | Modell bauen, lösen, Timeout, Fallback | Celery/Redis, OR-Tools{idx:OR-Tools}, HiGHS, CVXPY |
| **Disposition** | Ergebnis prüfen, ändern, freigeben | Web-UI, Dashboard, Freigabeworkflow |
| **Ausführung** | Kalender-Sync, Benachrichtigung, Order-Routing | Kalender-APIs, Messaging, Broker |
| **Betrieb** | Monitoring, Audit-Trail, Alarme | Logging, Metriken, Versionierung |
@ -1007,7 +1007,7 @@ zeigt nach innen auf die Domäne, und die Domäne zeigt auf nichts. Erzeugt von
Der unscheinbarste, aber wirksamste Teil ist die Statusübersetzung. Dieselbe Aussage heißt
in den fünf im Buch verwendeten Bibliotheken:
| Bedeutung | pywraplp | CP-SAT | HiGHS | SciPy | CVXPY |
| Bedeutung | pywraplp | CP-SAT | HiGHS{idx:HiGHS} | SciPy | CVXPY{idx:CVXPY} |
| --- | --- | --- | --- | --- | --- |
| beweisbar optimal | `Solver.OPTIMAL` | `cp_model.OPTIMAL` | `"Optimal"` | `status == 0` | `"optimal"` |
| zulässig, nicht bewiesen | `Solver.FEASIBLE` | `cp_model.FEASIBLE` | `"Time limit reached"` | — | `"optimal_inaccurate"` |
@ -1595,7 +1595,7 @@ deshalb nicht glauben sollte, bevor sie jemand vorgeführt hat.
Also führen wir sie vor — und zwar am unbequemsten Paar, das dieses Buch zu bieten hat:
**CP-SAT gegen HiGHS**. Die beiden sind grundverschieden. CP-SAT ist ein
Constraint-Programming-Solver, der ausschließlich ganzzahlig rechnet und Restriktionen als
Ausdrücke entgegennimmt. HiGHS ist ein klassischer Branch-and-Bound-Löser, der eine
Ausdrücke entgegennimmt. HiGHS ist ein klassischer Branch-and-Bound{idx:Branch-and-Bound}-Löser, der eine
Koeffizientenmatrix erwartet. Und sie lassen sich nicht einmal gemeinsam importieren
({ref:kap:oekosystem}).
@ -2044,10 +2044,10 @@ Arbeiten Sie diese Liste ab, bevor ein OR-System in Betrieb geht:
**Modell**
- [ ] Alle Nebenbedingungen sind dokumentiert — mit fachlicher Begründung, nicht nur als Code.
- [ ] Für jede harte Bedingung ist geprüft: Muss sie wirklich hart sein?
- [ ] Für jede harte Bedingung ist geprüft: Muss sie wirklich hart{idx:Nebenbedingung!hart} sein?
- [ ] Es gibt eine Relaxationsstrategie für den Infeasible-Fall.
- [ ] Big-M-Werte sind so klein wie möglich gewählt.
- [ ] Die Zielfunktion ist in konsistenten Einheiten formuliert.
- [ ] Die Zielfunktion{idx:Zielfunktion} ist in konsistenten Einheiten formuliert.
**Daten**
- [ ] Spaltenreihenfolge und Datentypen werden nach jedem Import geprüft.
@ -2074,7 +2074,7 @@ Arbeiten Sie diese Liste ab, bevor ein OR-System in Betrieb geht:
**Grundlagen und lineare/ganzzahlige Optimierung**
* Bertsimas & Tsitsiklis: *Introduction to Linear Optimization* — Standardwerk zu Simplex,
Dualität, Polyedertheorie.
* Wolsey: *Integer Programming* — Branch-and-Cut, Schnittebenen, Formulierungsstärke.
* Wolsey: *Integer Programming* — Branch-and-Cut{idx:Branch-and-Cut}, Schnittebenen, Formulierungsstärke.
**Konvexe Optimierung**
* Boyd & Vandenberghe: *Convex Optimization* — Pflichtlektüre; frei verfügbar. Die
@ -2084,8 +2084,8 @@ Arbeiten Sie diese Liste ab, bevor ein OR-System in Betrieb geht:
* Google OR-Tools Dokumentation und das CP-SAT-Primer von Laurent Perron.
**Quantitative Finanzmathematik**
* López de Prado: *Advances in Financial Machine Learning* — Backtest-Overfitting,
Denoising, Deflated Sharpe Ratio.
* López de Prado: *Advances in Financial Machine Learning* — Backtest-Overfitting{idx:Overfitting},
Denoising, Deflated Sharpe Ratio{idx:Sharpe-Ratio}.
* Cornuéjols, Peña & Tütüncü: *Optimization Methods in Finance*.
**Wie es weitergeht**
@ -2100,7 +2100,7 @@ Wenn Sie doch tiefer in die Methodik wollen, sind das die lohnendsten Richtungen
| --- | --- | --- |
| **Column Generation / Branch-and-Price**{idx:Column Generation} | sehr große Zuschnitt- und Dienstplanprobleme | {ref:kap:dekomposition}, Wolsey |
| **Benders-Zerlegung**{idx:Benders-Zerlegung} | zweistufige stochastische Modelle mit vielen Szenarien | Birge & Louveaux |
| **Approximate Dynamic Programming / RL**{idx:Approximate Dynamic Programming} | hochdimensionale Zustandsräume | Powell: *Reinforcement Learning and Stochastic Optimization* |
| **Approximate Dynamic Programming / RL**{idx:Approximate Dynamic Programming} | hochdimensionale Zustandsräume | Powell: *Reinforcement Learning{idx:Reinforcement Learning} and Stochastic Optimization* |
| **Konische Optimierung (SOCP, SDP)**{idx:Konische Optimierung (SOCP, SDP)} | robuste Portfolios, Ellipsoid-Unsicherheit | Boyd & Vandenberghe, {ref:kap:lp} |
| **Multi-Objective Optimization**{idx:Multi-Objective Optimization} | echte Zielkonflikte ohne Gewichtung | Ehrgott: *Multicriteria Optimization* |
@ -2110,16 +2110,16 @@ Wenn Sie doch tiefer in die Methodik wollen, sind das die lohnendsten Richtungen
> Lösungen: {ref:sec:loesungen-praxisfallen}.
**Aufgabe ⭐ — Hart oder weich, revisited.**
**Aufgabe ⭐ — Hart oder weich{idx:Nebenbedingung!weich}, revisited.**
Nennen Sie für ein Vertretungsplanungssystem je zwei Bedingungen, die (a) zwingend hart
bleiben müssen, (b) unbedingt weich sein sollten, (c) diskutabel sind. Begründen Sie.
**Aufgabe ⭐ — Zeitlimit wählen.**
Für welche Szenarien setzen Sie welches Zeitlimit und welchen Gap? (a) Taxi-Disposition in
Echtzeit, (b) Wochendienstplan, freitags erstellt, (c) Jahresproduktionsplanung,
(d) Portfolio-Rebalancing monatlich.
(d) Portfolio-Rebalancing{idx:Rebalancing} monatlich.
**Aufgabe ⭐⭐ — Relaxation einbauen.**
**Aufgabe ⭐⭐ — Relaxation{idx:Relaxation} einbauen.**
Nehmen Sie Ihr Modell aus der Aufgabe *Eigener Dienstplan*
({ref:sec:cpsat-uebungsaufgaben}) und machen Sie es
`INFEASIBLE`-sicher: Schlupfvariablen für unbesetzte Schichten, gestaffelte Strafen. Testen
@ -2146,8 +2146,8 @@ Sie Laufzeit und Zielwert bei Gap-Vorgaben von 10 %, 5 %, 2 %, 1 %, 0,1 % und 0
Stellen Sie den Zusammenhang grafisch dar und leiten Sie eine Empfehlung ab.
**Aufgabe ⭐⭐⭐ — Post-Mortem schreiben.**
Suchen Sie sich einen der im Buch besprochenen Fehler aus (z. B. die Vorzeichenfalle in
{ref:sec:lp-die-vorzeichenfalle-bei-schattenpreisen}, die ungültige Kovarianzmatrix in
Suchen Sie sich einen der im Buch besprochenen Fehler aus (z. B. die Vorzeichenfalle{idx:Schattenpreis!Vorzeichenfalle} in
{ref:sec:lp-die-vorzeichenfalle-bei-schattenpreisen}, die ungültige Kovarianzmatrix{idx:Kovarianzmatrix} in
{ref:kap:qp-nlp}, die Sektor-Positionsindizes in {ref:kap:markowitz}, die vermischten
Einheiten in {ref:sec:cvar-implementierung-cvar-portfolio-mit-reibung} oder die
Rebalancing-Termine in {ref:sec:handelsmaschine-rebalancing-termine-richtig-bestimmen})
@ -2472,7 +2472,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
> der produktivste erste Schritt?**
> (a) Das Zeitlimit erhöhen.
> (b) Die neue Regel wieder entfernen und in Ruhe abwarten.
> (c) Die harten Regeln hierarchisch lockern — mit teuren Strafkosten statt Verboten —,
> (c) Die harten Regeln hierarchisch lockern — mit teuren Strafkosten{idx:Strafkosten} statt Verboten —,
> damit der Solver benennt, **welche** Regel den Widerspruch erzeugt, statt nur zu sagen,
> dass es einen gibt.

View file

@ -100,7 +100,7 @@ Nach diesem Kapitel können Sie …
## Warum Optimierungsmodelle schwer zu testen sind {#sec:testing-warum-schwer}
Bei einer gewöhnlichen Funktion schreibt man den erwarteten Wert hin:
Bei einer gewöhnlichen Funktion{idx:Konvexität!Funktion} schreibt man den erwarteten Wert hin:
```python
assert steuer(50_000) == 12_345.60

View file

@ -7,11 +7,11 @@ werden. Was dabei schiefgeht, hat selten mit Mathematik zu tun.
| Was in Produktion passiert | Gegenmittel | Wo |
| --- | --- | --- |
| `INFEASIBLE` um 3 Uhr nachts, niemand weiß warum | Relaxation mit gestaffelten Strafkosten (B18); Deletion Filter für die Diagnose | {ref:kap:praxisfallen}, {ref:anhang:fehlerdiagnose} |
| Der Disponent lehnt den Plan ab, weil er ihn nicht versteht | Constraint-Trace für die einzelne Zuweisung; Kostenzurechnung für den ganzen Plan | {ref:kap:praxisfallen}, {ref:sec:praxisfallen-attribution} |
| `INFEASIBLE` um 3 Uhr nachts, niemand weiß warum | Relaxation{idx:Relaxation} mit gestaffelten Strafkosten{idx:Strafkosten} (B18); Deletion Filter{idx:Deletion Filter} für die Diagnose | {ref:kap:praxisfallen}, {ref:anhang:fehlerdiagnose} |
| Der Disponent lehnt den Plan ab, weil er ihn nicht versteht | Constraint-Trace{idx:Constraint-Trace} für die einzelne Zuweisung; Kostenzurechnung für den ganzen Plan | {ref:kap:praxisfallen}, {ref:sec:praxisfallen-attribution} |
| Der Solver läuft ins Zeitlimit und keiner merkt es | Status, Gap und Zeitausschöpfung protokollieren — konstante Laufzeit ist ein Warnsignal | {ref:kap:praxisfallen} |
| Ein Test wird grundlos mal rot, mal grün | auf Zielwert und Regeln prüfen, nicht auf die Gestalt der Lösung; `num_workers = 1` **und** Seed | {ref:sec:cpsat-parallele-suche}, {ref:kap:testing} |
| Ein Ergebnis lässt sich später nicht mehr nachvollziehen | Snapshot-Prinzip: unveränderlicher Datenstand mit ID je Lauf | {ref:kap:praxisfallen} |
| Ein Ergebnis lässt sich später nicht mehr nachvollziehen | Snapshot-Prinzip{idx:Snapshot-Prinzip}: unveränderlicher Datenstand mit ID je Lauf | {ref:kap:praxisfallen} |
| Das Modell prüft sich selbst | Abnahmeprüfung als eigener Baustein, ohne Solver und ohne Modellvariable | {ref:sec:praxisfallen-or-kern} |
## Was dieser Teil gemessen hat
@ -20,12 +20,12 @@ werden. Was dabei schiefgeht, hat selten mit Mathematik zu tun.
| --- | --- | --- |
| „Der Solver nennt schon den Grund für `INFEASIBLE`." | Er nennt gar nichts. Der Deletion Filter findet einen kleinsten Konflikt — dieses Modell enthält **neun** verschiedene, und welchen man sieht, steuert die Prüfreihenfolge | {ref:anhang:fehlerdiagnose} |
| „Bindende Bedingungen sind die teuren." | Von fünf bindenden Bedingungen kostet eine **0,00 €** | {ref:sec:praxisfallen-attribution} |
| „Der Schattenpreis sagt, was der Hebel bringt." | Hochgerechnet 120 €, gemessen **60 €** — er gilt nur bis +15 Stunden. Die Ranglisten nach Preis und nach Wirkung drehen sich um | {ref:sec:praxisfallen-attribution} |
| „Der Schattenpreis{idx:Schattenpreis} sagt, was der Hebel bringt." | Hochgerechnet 120 €, gemessen **60 €** — er gilt nur bis +15 Stunden. Die Ranglisten nach Preis und nach Wirkung drehen sich um | {ref:sec:praxisfallen-attribution} |
| „Mehr Arbeiter sind proportional schneller." | **12,3-fach** bei acht Arbeitern — überlinear, weil verschiedene Strategien statt derselben Suche laufen | {ref:sec:cpsat-parallele-suche} |
## Drei Fehler, die dieser Teil verhindert
1. **Alles hart formulieren.** Wer keine Regel brechen lässt, bekommt irgendwann eine
1. **Alles hart{idx:Nebenbedingung!hart} formulieren.** Wer keine Regel brechen lässt, bekommt irgendwann eine
Fehlermeldung statt eines Plans — und zwar nachts, im Batchlauf.
2. **Genauer optimieren als die Daten sind.** Ein Gap von 2 % ist bei ±10 %
Datenunsicherheit bedeutungslos. Die Rechenzeit dafür ist verschenkt.

View file

@ -54,11 +54,11 @@ Abwesenheitsmeldungen.
* Hart: genau eine Person je Slot; Qualifikation; keine Doppelbelegung; Höchstzahl
Vertretungsstunden; Ruhezeiten.
* Weich: Vorbelastung, Freistundenlöcher, Fachnähe, Wünsche, Fairness über die Woche.
* Solver: CP-SAT.
* Solver: CP-SAT{idx:CP-SAT}.
**Abnahmekriterien.**
- [ ] Läuft in unter 10 Sekunden für eine ganze Schule (60 Lehrkräfte, 30 Slots).
- [ ] Liefert **immer** einen Plan — auch wenn nicht alle Slots besetzbar sind (Relaxation).
- [ ] Liefert **immer** einen Plan — auch wenn nicht alle Slots besetzbar sind (Relaxation{idx:Relaxation}).
- [ ] Weist je Zuweisung eine Begründung aus.
- [ ] Ausgabe als CSV **und** als lesbare Tagestabelle.
- [ ] Vergleich gegen die manuelle Lösung: Wie viele Wünsche werden erfüllt?
@ -96,7 +96,7 @@ Arbeitszeitgesetz, Tarifvertrag, Qualifikationsmix und individuelle Wünsche.
- [ ] Bei Unlösbarkeit: Notfallplan plus benannte Ursache.
**Stolperfallen.** Der 28-Tage-Zyklus erzeugt viele Variablen (25 × 28 × 3 = 2100) —
Symmetriebrechung und gute Suchheuristiken werden wichtig. Fairness über einen Monat ist
Symmetriebrechung{idx:Symmetriebrechung} und gute Suchheuristiken werden wichtig. Fairness über einen Monat ist
etwas anderes als Fairness über eine Woche.
---
@ -111,8 +111,8 @@ an. Kunden haben Zeitfenster, Fahrzeuge Kapazitäten, Fahrer Arbeitszeiten.
**Daten.** Adressen aus einer CSV; Entfernungen über OpenStreetMap (`osmnx`, `openrouteservice`)
oder als Luftlinie mit Umwegfaktor 1,3 als Näherung.
**Modellskizze.** OR-Tools Routing-Bibliothek mit Kapazitäts- und Zeitdimension;
Metaheuristik `GUIDED_LOCAL_SEARCH`, Zeitlimit 30 Sekunden.
**Modellskizze.** OR-Tools{idx:OR-Tools} Routing-Bibliothek{idx:Routing-Bibliothek} mit Kapazitäts- und Zeitdimension;
Metaheuristik{idx:Metaheuristik} `GUIDED_LOCAL_SEARCH`, Zeitlimit 30 Sekunden.
**Abnahmekriterien.**
- [ ] Alle Kunden werden innerhalb ihrer Zeitfenster beliefert.
@ -136,12 +136,12 @@ eröffnet werden sollen, um 60 Filialen zu versorgen.
**Modellskizze.** Kombiniertes Standort- und Transportproblem:
* $y_j \in \{0,1\}$ — Lager $j$ eröffnen (Fixkosten).
* $x_{ij} \ge 0$ — Menge von Lager $j$ zu Filiale $i$.
* $x_{ij} \ge 0$ — Menge{idx:Konvexität!Menge} von Lager $j$ zu Filiale $i$.
* Kopplung: $x_{ij} \le M\,y_j$; Kapazität je Lager; Bedarfsdeckung je Filiale.
* Ziel: Fixkosten + Transportkosten minimieren.
**Abnahmekriterien.**
- [ ] Sensitivitätsanalyse: Wie verändert sich die Lösung bei ±20 % Transportkosten?
- [ ] Sensitivitätsanalyse{idx:Sensitivitätsanalyse}: Wie verändert sich die Lösung bei ±20 % Transportkosten?
- [ ] Was-wäre-wenn: Standort X wird politisch vorgegeben — was kostet das?
- [ ] Kartendarstellung mit Zuordnungslinien.
- [ ] Amortisationsrechnung über 10 Jahre.
@ -157,7 +157,7 @@ nicht einfach addieren.
> **Schwierigkeit:** ⭐⭐⭐ · **Kapitel:** {ref:kap:lp}, {ref:kap:milp}, {ref:kap:cpsat} · **Zeit:** ca. 18 Stunden
**Ausgangslage.** Eine Fertigung produziert 15 Varianten auf 4 Maschinen. Jeder
Produktwechsel kostet Rüstzeit; die Rüstzeit hängt von der Reihenfolge ab.
Produktwechsel kostet Rüstzeit{idx:Rüstzeit}; die Rüstzeit hängt von der Reihenfolge ab.
**Modellskizze.** Los- und Reihenfolgeplanung:
* Variablen: Produktionsmengen, Rüstentscheidungen, Reihenfolge (Intervallvariablen).
@ -187,9 +187,9 @@ Steuerfreibetragsnutzung.
**Modellskizze.**
* Variablen: Zielgewichte $w_i$; Binärvariablen für „Position wird gehandelt“.
* Hart: Vollinvestition, keine Leerverkäufe, Positionsobergrenzen, Mindestordergröße
(semikontinuierlich, {ref:kap:milp}), höchstens $K$ Transaktionen je Rebalancing.
* Ziel: erwartete Rendite Risikoterm Transaktionskosten Steuerwirkung.
* Solver: CVXPY (konvexer Teil) oder MIQP für die Kardinalität.
(semikontinuierlich, {ref:kap:milp}), höchstens $K$ Transaktionen je Rebalancing{idx:Rebalancing}.
* Ziel: erwartete Rendite Risikoterm Transaktionskosten{idx:Transaktionskosten} Steuerwirkung.
* Solver: CVXPY{idx:CVXPY} (konvexer Teil) oder MIQP für die Kardinalität.
**Abnahmekriterien.**
- [ ] Toleranzband: Es wird nur gehandelt, wenn die Abweichung > 5 Prozentpunkte beträgt.
@ -224,7 +224,7 @@ Ihre Strategie die Gleichgewichtung nicht schlägt, ist das ein Ergebnis, kein M
- [ ] Klartext-Zusammenfassung: „Im schlechtesten Prozent der Monate verlieren Sie
typischerweise X €.“
**Stolperfallen.** Die Wurzel-Zeit-Regel bei CVaR ({ref:kap:cvar}) — nicht blind anwenden.
**Stolperfallen.** Die Wurzel-Zeit-Regel{idx:Wurzel-Zeit-Regel} bei CVaR ({ref:kap:cvar}) — nicht blind anwenden.
Korrelationen sind in Krisen andere als im Mittel: Stresstest mit erhöhten Korrelationen
rechnen.
@ -276,7 +276,7 @@ genau die Frage, um die es geht.
Kilogramm CO₂ an Mehrkosten bedeutet.
**Abnahmekriterien.**
- [ ] Die Pareto-Front ist berechnet und gezeichnet, nicht nur beschrieben.
- [ ] Die Pareto-Front{idx:Pareto-Front} ist berechnet und gezeichnet, nicht nur beschrieben.
- [ ] Für mindestens zwei Punkte ist der Aufpreis je eingesparter Einheit ausgerechnet.
- [ ] Es ist geprüft und dokumentiert, welche Punkte der Front eine **gewichtete Summe
niemals** finden würde — und warum.
@ -296,7 +296,7 @@ Modell, sondern der Schritt danach.
**Modellskizze.** Vier Schichten, jede einzeln abnehmbar:
* **Domäne:** Eingabedaten als validierte Objekte (Pydantic), nicht als lose Dictionaries —
ein falscher Wert soll beim Einlesen auffallen, nicht im Solver ({ref:kap:praxisfallen}).
* **Kern:** Modellaufbau und Lösung hinter einer Funktion, die ein `Loesung`-Objekt
* **Kern:** Modellaufbau und Lösung hinter einer Funktion{idx:Konvexität!Funktion}, die ein `Loesung`-Objekt
zurückgibt und **alle** Statusfälle behandelt.
* **Tests:** eine Suite, die auch die Fälle abdeckt, in denen es *keine* Lösung gibt
({ref:kap:testing}).

View file

@ -17,10 +17,10 @@
(d)/(e): Die Prognose sagt, *wer* kündigen wird; die Optimierung sagt, *wen* man mit dem
vorhandenen Geld halten kann.
**{loesung} — Hart oder weich?**
(a) **hart** — gesetzlich zwingend. (b) **weich** — Wunsch, mit Strafkosten. (c) **hart**
**{loesung} — Hart oder weich{idx:Nebenbedingung!weich}?**
(a) **hart** — gesetzlich zwingend. (b) **weich** — Wunsch, mit Strafkosten{idx:Strafkosten}. (c) **hart**
Patientensicherheit, rechtlich vorgeschrieben. (d) **weich** — Fairnessziel, über
Strafterme. (e) **hart**, falls tariflich/gesetzlich fixiert, sonst weich mit sehr hoher
Strafterme. (e) **hart**{idx:Nebenbedingung!hart}, falls tariflich/gesetzlich fixiert, sonst weich mit sehr hoher
Strafe. Faustregel: Hart ist nur, was rechtlich oder physikalisch unmöglich zu verletzen
ist.
@ -33,8 +33,8 @@ Jeder weitere Auftrag multipliziert die Zeit mit der neuen Anzahl.
$14! = 8{,}7\cdot10^{10}$ ✗. Also **13 Aufträge**.
**{loesung} — Modell lesen.**
(a) Variablen $x_1, x_2 \ge 0$; Parameter $(3,5)$ und die Kapazitäten $(4,12,18)$;
Zielfunktion $\max 3x_1+5x_2$; vier Nebenbedingungen inkl. Nichtnegativität.
(a) Variablen $x_1, x_2 \ge 0$; Parameter{idx:Parameter (Modell)} $(3,5)$ und die Kapazitäten $(4,12,18)$;
Zielfunktion{idx:Zielfunktion} $\max 3x_1+5x_2$; vier Nebenbedingungen inkl. Nichtnegativität.
(b) $(2,6)$: $2\le4$ ✓, $12\le12$ ✓, $6+12=18\le18$ ✓ → zulässig, $Z=36$.
$(4,3)$: $4\le4$ ✓, $6\le12$ ✓, $12+6=18\le18$ ✓ → zulässig, $Z=27$.
(c) Beste ganzzahlige Lösung ist $(2,6)$ mit $Z=36$ — sie ist hier bereits ganzzahlig, weil
@ -73,7 +73,7 @@ $Z = 2{,}5\cdot96 + 3\cdot70 = 240 + 210 = 450$ ✓.
Schleife über `RAM_GESAMT in range(54, 73, 2)`, jeweils Modell neu lösen.
(a) Ab 62 GB steigt der Gewinn nicht mehr — dann bindet die **vCPU**-Grenze, RAM ist nicht
mehr der Engpass.
(b) Der Zuwachs je GB ist der **Schattenpreis** ({ref:kap:lp}). Solange RAM bindet, liegt er
(b) Der Zuwachs je GB ist der **Schattenpreis**{idx:Schattenpreis} ({ref:kap:lp}). Solange RAM bindet, liegt er
bei 31,25 €/GB; danach fällt er auf 0.
**{loesung} — Eigenes Problem.** Individuell. Prüfkriterien: Sind die Variablen wirklich
@ -150,9 +150,9 @@ Grund für die Rechenzeit.
---
## Lösungen zu Kapitel „Das mathematische Fundament — Vektoren, Matrizen, Konvexität“ {#sec:loesungen-fundament}
## Lösungen zu Kapitel „Das mathematische Fundament — Vektoren, Matrizen, Konvexität{idx:Konvexität}“ {#sec:loesungen-fundament}
**{loesung} — Matrixform lesen.**
**{loesung} — Matrixform{idx:Matrixform} lesen.**
$\max 4x_1 + x_2 + 6x_3$ u. d. N. $x_1 + 2x_2 \le 10$, $x_2 + 3x_3 \le 12$, $x \ge 0$.
**3 Variablen, 2 Nebenbedingungen** (plus Nichtnegativität).
@ -175,9 +175,9 @@ aber weiterhin mindestens eine in einer Ecke — der Fundamentalsatz bleibt gül
(c) **nicht** konvex, sondern **konkav** ($f'' = -\tfrac14 x^{-3/2} < 0$).
(d) **nicht** konvex, konkav ($f'' = -1/x^2 < 0$).
(e) konvex (Summe konvexer Funktionen; Hesse-Matrix $2\mathbf{I} \succ 0$).
(f) konvexe Menge (Kreisscheibe).
(f) konvexe Menge{idx:Konvexität!Menge} (Kreisscheibe).
(g) **konvexe Menge** — der Bereich oberhalb der Hyperbel im positiven Quadranten ist
konvex (Achtung, überraschend: die *Funktion* $1/x$ ist konvex, und die Menge
konvex (Achtung, überraschend: die *Funktion*{idx:Konvexität!Funktion} $1/x$ ist konvex, und die Menge
$\{x_2 \ge 1/x_1\}$ ist der Epigraph einer konvexen Funktion, also konvex).
**{loesung} — Positive Semidefinitheit.**
@ -256,7 +256,7 @@ sagt, *wo* man suchen muss, nicht dass die Suche billig ist.
**2 — (b).** Faustregel $\kappa = 10^{k}$ ⟹ etwa $k$ signifikante Stellen verloren; bei
$10^{11}$ bleiben von 16 rund 5. Das Modell ist deshalb nicht unlösbar (a) — es rechnet nur
mit einer Genauigkeit, die den Ergebnissen nicht mehr anzusehen ist. (c) verwechselt die
Konditionszahl mit einem Aufwandsmaß; sie sagt nichts über die Iterationszahl.
Konditionszahl{idx:Konditionszahl} mit einem Aufwandsmaß; sie sagt nichts über die Iterationszahl.
**3 — (c).** `int()` schneidet ab: `int(0.99999998) == 0` macht aus einer Ja- eine
Nein-Entscheidung (a). Den Wert unverändert weiterzureichen (b) verschiebt das Problem nur
@ -281,9 +281,9 @@ ist die Prüfung gegen die Toleranz mit Fehlermeldung im Zweifelsfall — die Fu
## Lösungen zu Kapitel „Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten“ {#sec:loesungen-oekosystem}
**{loesung} — Solverwahl.**
(a) CP-SAT (diskrete Zuordnung mit Zeitfenstern).
(a) CP-SAT{idx:CP-SAT} (diskrete Zuordnung mit Zeitfenstern).
(b) `scipy.optimize.linprog` (klassisches Mischungs-LP, klein).
(c) CVXPY (konvexes QP).
(c) CVXPY{idx:CVXPY} (konvexes QP).
(d) MIQP — CVXPY mit Binärvariablen und MIQP-fähigem Solver, oder Heuristik.
(e) MILP über `highspy` oder CP-SAT (Standortproblem mit Fixkosten).
(f) `scipy.optimize.minimize` mit Multistart (nicht konvex).
@ -292,7 +292,7 @@ ist die Prüfung gegen die Toleranz mit Fehlermeldung im Zweifelsfall — die Fu
(kontinuierlich) liefern. Denken Sie an die Prozesstrennung, falls `ortools` und `highspy`
kollidieren.
**{loesung} — CSR-Format.**
**{loesung} — CSR-Format{idx:CSR-Format}.**
`values = [3, 1, 2, 5, 4, 6]`, `indices = [0, 3, 2, 0, 1, 3]`, `starts = [0, 2, 3]`.
CSR speichert 6 Werte + 6 Indizes + 3 Startpositionen = 15 Zahlen; die volle Matrix hätte
$3\times4 = 12$. **Bei dieser winzigen, dicht besetzten Matrix lohnt CSR nicht** — der
@ -311,8 +311,8 @@ der Modellaufbau effizienter ist. CVXPY hat den größten festen Aufwand
(Ausdrucksbaum-Kompilierung), der bei wiederholten Läufen mit `cp.Parameter` teilweise
entfällt.
**{loesung} — Eigene Entscheidungshilfe.** Ergänzungen: Bei kommerzieller Lizenz Gurobi/CPLEX
über Pyomo oder CVXPY empfehlen; bei Lesbarkeitsanforderung Pyomo (algebraische Notation,
**{loesung} — Eigene Entscheidungshilfe.** Ergänzungen: Bei kommerzieller Lizenz Gurobi{idx:Gurobi}/CPLEX
über Pyomo{idx:Pyomo} oder CVXPY empfehlen; bei Lesbarkeitsanforderung Pyomo{idx:Pyomo} (algebraische Notation,
Trennung von Daten und Modell).
### Finde den Denkfehler — Der Solver, der angeblich dreimal schneller ist
@ -377,7 +377,7 @@ Sekunden — selbst bei Faktor 4 gewinnen Sie 6 der 40 Sekunden. Das Zeitlimit (
den Abbruch, nicht die Geschwindigkeit. Die Regel dahinter: **erst messen, wo die Zeit
hingeht, dann optimieren.**
**3 — (b).** Alle vier sind Modellierungsschichten über HiGHS. Deshalb liefern sie
**3 — (b).** Alle vier sind Modellierungsschichten über HiGHS{idx:HiGHS}. Deshalb liefern sie
denselben Zielwert (bis auf Toleranz) und unterscheiden sich in der reinen Rechenzeit kaum
— wohl aber im Aufbauaufwand und in der Lesbarkeit. (a) ist falsch: Niemand von ihnen
implementiert den Simplex in Python, das wäre um Größenordnungen zu langsam. (c) ist
@ -394,7 +394,7 @@ es spezialisierte globale Solver ({ref:kap:qp-nlp}).
4. Wegen des Kompilierungsaufwands des Ausdrucksbaums. Kein Argument dagegen, weil dieser
Aufwand einmalig ist, die Lesbarkeit hoch und die Konvexitätsprüfung wertvoll.
5. Bei rein kontinuierlichen Problemen (CP-SAT kennt nur ganze Zahlen) und bei reinen
Routing-Problemen (dort ist die Routing-Bibliothek überlegen).
Routing-Problemen (dort ist die Routing-Bibliothek{idx:Routing-Bibliothek} überlegen).
---
@ -577,13 +577,13 @@ Ressource 3 — der höchste Schattenpreis (9,8) bedeutet den größten Grenznut
zurückgedreht. Der korrekte Schattenpreis ist **+45 €**.
**{loesung} — Simplex von Hand.**
Starttableau mit $s_1, s_2$ in der Basis. Erste Iteration: Pivotspalte $x_1$ ($-5$),
Quotienten $24/6 = 4$ und $6/1 = 6$ → Pivotzeile 1. Nach dem Tausch: $x_1 = 4$, $Z = 20$.
Starttableau mit $s_1, s_2$ in der Basis. Erste Iteration: Pivotspalte{idx:Simplex-Algorithmus!Pivotspalte} $x_1$ ($-5$),
Quotienten $24/6 = 4$ und $6/1 = 6$ → Pivotzeile{idx:Simplex-Algorithmus!Pivotzeile} 1. Nach dem Tausch: $x_1 = 4$, $Z = 20$.
Zweite Iteration: Pivotspalte $x_2$, Pivotzeile 2 → $x_1 = 3$, $x_2 = 1{,}5$, $Z = 21$.
**Optimum:** $\mathbf{x}^* = (3;\ 1{,}5)$, $Z^* = 21$, Schattenpreise
$y^* = (0{,}75;\ 0{,}50)$.
**{loesung} — Duales Problem.**
**{loesung} — Duales Problem{idx:Duales Problem}.**
$\min 24y_1 + 6y_2$ u. d. N. $6y_1 + y_2 \ge 5$, $4y_1 + 2y_2 \ge 4$, $y \ge 0$.
Lösung $y^* = (0{,}75;\ 0{,}50)$. Probe der Restriktionen:
$6\cdot0{,}75 + 0{,}5 = 5$ ✓ (mit Gleichheit, weil $x_1 > 0$),
@ -672,7 +672,7 @@ vorhanden, muss der Dualwert null sein. (a) hat die Aussage in ihr Gegenteil ver
verwechselt Schlupf mit Auslastung — $s_i = 12$ heißt gerade, dass 12 Einheiten *übrig*
sind.
**2 — (b) Entartung.** 7 aktive Bedingungen bei 5 Variablen bedeuten: Die Ecke ist
**2 — (b) Entartung{idx:Entartung}.** 7 aktive Bedingungen bei 5 Variablen bedeuten: Die Ecke ist
überbestimmt, es gibt mehrere optimale Dualvektoren, und welchen der Solver zeigt, hängt
vom gewählten Verfahren ab. (a) verharmlost genau den Fall, der Fehlentscheidungen
produziert; (c) verwechselt Entartung mit Unlösbarkeit — der Plan selbst ist völlig in
@ -697,7 +697,7 @@ abgesehen von genau diesem Rauschen.
---
## Lösungen zu Kapitel „Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound“ {#sec:loesungen-milp}
## Lösungen zu Kapitel „Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound{idx:Branch-and-Bound}“ {#sec:loesungen-milp}
**{loesung} — Runden widerlegen.**
Beispiel: $\max x_1 + x_2$ u. d. N. $10x_1 + 10x_2 \le 15$, ganzzahlig. LP-Optimum
@ -706,7 +706,7 @@ Optimum ist $(1,0)$ mit $Z = 1$. Prinzip: kleine Zahlen plus knappe Kapazität.
**{loesung} — Big-M wählen.**
$M = 250$ (die bekannte Kapazität). Bei $M = 10^6$ bleibt das Modell korrekt, aber die
LP-Relaxation wird extrem schwach: $y_j$ darf schon bei $x_j/10^6$ liegen, die Schranke ist
LP-Relaxation{idx:LP-Relaxation} wird extrem schwach: $y_j$ darf schon bei $x_j/10^6$ liegen, die Schranke ist
praktisch wertlos, und Branch-and-Bound muss weit mehr Knoten durchsuchen.
**{loesung} — Regeln übersetzen.**
@ -717,7 +717,7 @@ praktisch wertlos, und Branch-and-Bound muss weit mehr Knoten durchsuchen.
(e) $\sum_{j=1}^3 y_j = 1$.
**{loesung} — Branch-and-Bound.**
LP-Relaxation der Wurzel: nach Nutzen/Gewicht sortieren ($8/5=1{,}6$; $11/7=1{,}57$;
LP-Relaxation{idx:Relaxation} der Wurzel: nach Nutzen/Gewicht sortieren ($8/5=1{,}6$; $11/7=1{,}57$;
$6/4=1{,}5$; $4/3=1{,}33$). Gierig füllen: $x_1=1$ (Rest 9), $x_2=1$ (Rest 2),
$x_3 = 0{,}5$ → $Z_{LP} = 8+11+3 = 22$. Verzweigen über $x_3$.
Ast $x_3=0$: $x_1=1,x_2=1,x_4=2/3$ → $Z = 21{,}67$; weiter verzweigen → beste ganzzahlige
@ -810,7 +810,7 @@ Schranke mit einem erreichbaren Wert: 47 100 € ist eine *untere* Schranke, es
offen, ob ein Plan mit diesen Kosten überhaupt existiert.
**2 — (b).** Die Laufzeit (a) ist ein realer, aber beherrschbarer Nachteil — man merkt ihn
und kann reagieren. Der Trickle Flow dagegen ist **still**: Das Modell meldet `Optimal`,
und kann reagieren. Der Trickle Flow{idx:Trickle Flow} dagegen ist **still**: Das Modell meldet `Optimal`,
liefert eine Zahl, und niemand sieht, dass die Fixkosten verschwunden sind. Ein Fehler, den
man bemerkt, ist immer harmloser als einer, den man nicht bemerkt. (c) ist frei erfunden —
negative Kosten entstehen dabei nicht.
@ -819,12 +819,12 @@ negative Kosten entstehen dabei nicht.
und verworfen, wenn er nicht zulässig ist. Das Ergebnis bleibt in jedem Fall korrekt (b ist
also falsch), und `INFEASIBLE` bezieht sich immer auf das Modell, nie auf einen Hinweis (a
ist falsch). Praktische Konsequenz: Prüfen Sie Ihre Startlösung selbst auf Zulässigkeit —
sonst messen Sie einen Warm-Start-Effekt, den es gar nicht gibt.
sonst messen Sie einen Warm-Start{idx:Warm-Start}-Effekt, den es gar nicht gibt.
### Selbsttest {#selbsttest-loesung-milp}
1. Weil die Relaxation **mehr** Lösungen zulässt (alle ganzzahligen plus gebrochene) — das
Maximum über einer größeren Menge ist mindestens so groß.
2. Teilproblem unzulässig; Schranke schlechter als der Incumbent; LP-Lösung bereits
2. Teilproblem unzulässig; Schranke schlechter als der Incumbent{idx:Branch-and-Bound!Incumbent}; LP-Lösung bereits
ganzzahlig.
3. $L\,y \le x \le U\,y$ mit binärem $y$.
4. Weil die LP-Relaxation dadurch schwächer wird: Die berechnete Schranke liegt weiter vom
@ -852,7 +852,7 @@ Zuweisungssummen koppeln, dann `modell.Add(arbeitet_bauer + arbeitet_koch <= 1)`
(c) Belohnung = negative Strafe: `strafterme.append(-30 * folge_var)`, wobei `folge_var`
per `AddBoolAnd`/`OnlyEnforceIf` an $x_{p,0} \wedge x_{p,1}$ gekoppelt wird.
**{loesung} — Infeasibility.**
**{loesung} — Infeasibility{idx:Infeasibility}.**
Mit `MAX_VERTRETUNGEN = 0` meldet der Solver `INFEASIBLE`. Nach Einbau der Schlupfvariablen
lässt der Solver **alle vier** Stunden ausfallen (4 × 10 000 = 40 000 Strafpunkte) — es geht
nicht anders. Interessanter wird es bei `MAX_VERTRETUNGEN = 1`: Dann fällt genau die Stunde
@ -892,7 +892,7 @@ Reproduzierbarkeit:
Nur die Kombination trägt.
(c) Bei **15 Aufträgen** (60 s Zeitlimit): Ein Arbeiter läuft ins Limit und meldet
`FEASIBLE` mit Makespan 200; acht Arbeiter melden `OPTIMAL` mit **demselben** Makespan 200
`FEASIBLE` mit Makespan{idx:Makespan} 200; acht Arbeiter melden `OPTIMAL` mit **demselben** Makespan 200
nach 26,4 s. Bei 13 Aufträgen sind es 11,2 s gegen 2,8 s, bei 14 schon 21,4 s gegen 3,7 s.
Bemerkenswert ist, dass beide denselben Wert finden — der Unterschied liegt nicht in der
Lösung, sondern im **Beweis**, dass es keine bessere gibt. Genau diese Arbeit teilen sich
@ -1012,7 +1012,7 @@ Regeln. Wer eine reproduzierbare Ausgabe braucht — etwa für ein Buch —, fix
## Lösungen zu Kapitel „Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP“ {#sec:loesungen-graphen}
**{loesung} — Flusserhaltung.** $b_i = (15+3) - (12+8) = -2$ → **Senke** (Nettobedarf 2).
**{loesung} — Flusserhaltung{idx:Flusserhaltung}.** $b_i = (15+3) - (12+8) = -2$ → **Senke** (Nettobedarf 2).
**{loesung} — Unlösbarkeit.** Die Summe aller Flusserhaltungsgleichungen ergibt $\sum_i b_i = 0$
(jede Kante taucht einmal mit $+1$ und einmal mit $-1$ auf). Ist die Summe ungleich null,
@ -1051,12 +1051,12 @@ Weg).
Auslastung deutlich. (b) Unlösbar, sobald $\text{Kapazität} \times \text{Fahrzeuge} <
\text{Gesamtbedarf}$ (hier: bei 3 Fahrzeugen à 10 sind 30 < 37 → unlösbar) **oder** wenn
Zeitfenster nicht mehr eingehalten werden können. (c) 1 s liefert meist eine brauchbare,
30 s eine spürbar bessere Lösung — die Metaheuristik verbessert kontinuierlich.
30 s eine spürbar bessere Lösung — die Metaheuristik{idx:Metaheuristik} verbessert kontinuierlich.
(d) Doppelte Servicezeit kann Zeitfenster verletzen → möglicherweise unlösbar.
**{loesung} — TSP mit MTZ.** Erwartetes Ergebnis: Bei 8 Städten löst das MILP in Sekunden. Ab
etwa 1215 Städten wird die schwache MTZ-Relaxation zum Problem, und die Laufzeit steigt
stark — während OR-Tools weiterhin in Sekundenbruchteilen sehr gute Touren liefert.
stark — während OR-Tools{idx:OR-Tools} weiterhin in Sekundenbruchteilen sehr gute Touren liefert.
### Finde den Denkfehler — Die vergessene Dimension
@ -1345,7 +1345,7 @@ kostet bis zu eine Rolle, und in der Summe sind es sechs.
Die Schranke sagt: *Weniger als 73 Rollen sind unmöglich.* Sie sagt **nicht**: *Jede
zulässige Lösung liegt höchstens eine Rolle darüber.* Das ist die Verwechslung von unterer
Schranke und Gütegarantie — dieselbe Verwechslung wie beim MIP-Gap in {ref:sec:milp-gap},
Schranke und Gütegarantie — dieselbe Verwechslung wie beim MIP-Gap{idx:MIP-Gap} in {ref:sec:milp-gap},
nur in die andere Richtung.
**Was der Kollege stattdessen hätte tun müssen:** das Master ein zweites Mal lösen, diesmal
@ -1362,7 +1362,7 @@ Zeile, dauert Millisekunden und liefert 73 statt 79. Er hatte alles dafür berei
Objekte — die Symmetrie verschwindet. (a) trifft zufällig auch zu, ist aber nicht der Grund;
(c) ist falsch, die LP-Lösung ist hier gerade **nicht** ganzzahlig (72,92).
**2. b)** Ein Rucksackproblem: Nutzen sind die Schattenpreise, Gewichte die Breiten,
**2. b)** Ein Rucksackproblem{idx:Rucksackproblem}: Nutzen sind die Schattenpreise, Gewichte die Breiten,
Kapazität die Rollenbreite. (c) beschreibt eine Heuristik — dann wäre das Verfahren nicht mehr
exakt, weil man nie sicher wüsste, ob es wirklich kein lohnendes Muster mehr gibt.
@ -1396,7 +1396,7 @@ konvex; die Lösung ist **nicht notwendig eindeutig**. Der Eigenwert 0 bedeutet
**flache Richtung**: Entlang des zugehörigen Eigenvektors ändert sich der quadratische Term
nicht — es gibt eine Rinne statt eines Punktes.
**{loesung} — Komplementärer Schlupf.** Ja, verträglich: $\lambda_i w_i = 0$ gilt für alle $i$
**{loesung} — Komplementärer Schlupf{idx:Komplementärer Schlupf}.** Ja, verträglich: $\lambda_i w_i = 0$ gilt für alle $i$
($w_2 = 0$ mit $\lambda_2 > 0$; die anderen mit $\lambda = 0$). $\lambda_2 = 0{,}03$
bedeutet: Würde man Titel 2 zwingen, ein kleines positives Gewicht zu tragen, verschlechterte
sich der Zielwert um 0,03 je Einheit — die Nichtnegativitätsschranke ist dort **bindend**.
@ -1431,7 +1431,7 @@ Gewichten negativ werden (der zugehörige Eigenvektor liegt allerdings teilweise
des zulässigen Bereichs $w \ge 0{,}001$, $\sum w = 1$ — deshalb fällt der Fehler bei
naiver Prüfung nicht auf).
### Finde den Denkfehler — Die Kovarianzmatrix aus dem Controlling
### Finde den Denkfehler — Die Kovarianzmatrix{idx:Kovarianzmatrix} aus dem Controlling
(a) **Ohne zu rechnen.** Stellen Sie sich die drei Anlagen als drei Personen vor, die
nebeneinander gehen. A und B gehen fast im Gleichschritt ($0{,}9$). B und C gehen fast im
@ -1515,7 +1515,7 @@ das im Bericht kenntlich machen.
nicht die Qualität des Ergebnisses. Bei Mengenrabatten ist die Zielfunktion nicht konvex,
also kann es mehrere lokale Minima geben — im Kapitelbeispiel fünf, mit 8 % Spanne. (a) wäre
nur bei einem konvexen Problem richtig. (c) ist zu pessimistisch: Ein lokales Minimum ist es
sehr wohl, die Zulässigkeit wird von SLSQP eingehalten.
sehr wohl, die Zulässigkeit wird von SLSQP{idx:SLSQP} eingehalten.
**3 — (c) Schattenpreis.** $\lambda^*$ ist die Ableitung des optimalen Zielwerts nach der
rechten Seite der Nebenbedingung — dieselbe Bedeutung wie der Dualwert im LP
@ -1550,7 +1550,7 @@ Projektkosten mit Nachträgen.
(b) stochastisch (Verteilung bekannt).
(c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen).
(d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor).
(e) Chance Constraint — die Zusage ist bereits als Quote formuliert („an 99 % aller
(e) Chance Constraint{idx:Chance Constraint} — die Zusage ist bereits als Quote formuliert („an 99 % aller
Wintertage“). Robust wäre hier zu teuer (es gibt immer einen kälteren Tag), der
Erwartungswert zu schwach (er sagt nichts über die Zusage).
@ -1595,7 +1595,7 @@ Prozentpunkt ist bereits 4,5-mal so teuer wie der erste. Sagen Sie mir, was ein
das Unternehmen kostet, dann rechne ich aus, welche Quote sich lohnt. Und die Zusage gilt
nur für die Wetterlagen, die wir modelliert haben — die Kältewelle gehört hinein.“
**{loesung} — Budgeted Uncertainty.**
**{loesung} — Budgeted Uncertainty{idx:Budgeted Uncertainty}.**
```python
abzug = cp.sum_largest(cp.multiply(UNSICHERHEIT, w), Gamma)
ziel = cp.Maximize(MU_SCHAETZUNG @ w - abzug - 0.5*LAMBDA*cp.quad_form(w, SIGMA))
@ -1620,7 +1620,7 @@ Gewerk, das 17 Tage braucht, hält alle auf. Die guten Ausreißer verpuffen, die
schlagen voll durch.
Damit gilt für das Maximum immer $\mathbb{E}[\max] \ge \max(\mathbb{E})$. Die Differenz ist
kein Schätzfehler, sondern eine **systematische Verzerrung** — sie geht immer in dieselbe
kein Schätzfehler{idx:Schätzfehler}, sondern eine **systematische Verzerrung** — sie geht immer in dieselbe
Richtung, und sie wächst mit der Streuung der Einzelschätzungen.
(b) **Warum 95,5 %.** Damit das Projekt die geplanten 11,33 Tage hält, müssen **alle fünf**
@ -1683,7 +1683,7 @@ hier 38 Stück. (a) ignoriert die Kostenasymmetrie und kostet im Kapitelbeispiel
setzt eine Wahrscheinlichkeitsverteilung voraus und minimiert den Erwartungswert; robuste
Optimierung kommt mit einer bloßen Bandbreite aus und sichert den ungünstigsten Fall darin
ab. Keines ist „genauer“ (a) — sie beantworten verschiedene Fragen. (c) trifft es nicht:
Robuste Optimierung betrachtet nicht mehr Szenarien, sondern eine ganze Menge auf einmal,
Robuste Optimierung{idx:Robuste Optimierung} betrachtet nicht mehr Szenarien, sondern eine ganze Menge auf einmal,
und interessiert sich darin nur für den schlechtesten Punkt.
**3 — (c).** Bei durchgehender Linearität und ohne nachgelagerte Entscheidung gilt
@ -1719,20 +1719,20 @@ Fall, in dem es tatsächlich zulässig ist.
---
## Lösungen zu Kapitel „Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution“ {#sec:loesungen-dynamische-programmierung}
## Lösungen zu Kapitel „Dynamische Programmierung — Die Bellman-Gleichung{idx:Bellman-Gleichung} und Order-Execution“ {#sec:loesungen-dynamische-programmierung}
**{loesung} — Bausteine.** Stufe = Tag 15; Zustand = verbleibende Distanz (ggf. plus
Erschöpfungsgrad); Aktion = heutige Tagesetappe; Wertfunktion = minimale Restanstrengung.
**Nicht** in den Zustand gehören: bereits gelaufene Kilometer (redundant, wenn die
Restdistanz bekannt ist) und das Wetter von gestern (ohne Einfluss auf die Zukunft).
**{loesung} — Optimalitätsprinzip.** Weil der Wert eines Zustands nur von den *künftigen*
**{loesung} — Optimalitätsprinzip{idx:Optimalitätsprinzip}.** Weil der Wert eines Zustands nur von den *künftigen*
Entscheidungen abhängt, kann man ihn ab dem Ende rekursiv berechnen. Hängen die Kosten
zusätzlich von **früheren** Aktionen ab, ist die Markov-Eigenschaft verletzt — die Lösung:
die relevante Vergangenheit **in den Zustand aufnehmen** (dann wächst allerdings der
Zustandsraum).
Zustandsraum{idx:Zustandsraum}).
**{loesung} — Rückwärtsinduktion.** Mit $C(n) = n^2 + 2n$ und 5 Einheiten in 3 Perioden ergibt
**{loesung} — Rückwärtsinduktion{idx:Rückwärtsinduktion}.** Mit $C(n) = n^2 + 2n$ und 5 Einheiten in 3 Perioden ergibt
sich als optimaler Pfad $2 \to 2 \to 1$ (oder eine Permutation davon) mit Gesamtkosten
$8 + 8 + 3 = 19$. Alles auf einmal kostet $25 + 10 = 35$.
@ -1807,7 +1807,7 @@ def V(t, lager, lief_vorher):
Damit findet die Rückwärtsinduktion $(3, 1, 4, 2)$ für 70 € — das nachgerechnete Optimum.
Der Zustandsraum **verdoppelt** sich (je Lagerstand zwei Varianten, „lief“ und „lief
nicht“). Das ist der übliche Preis: Vollständigkeit des Zustands kostet Größe, und genau
hier beginnt der Fluch der Dimensionalität.
hier beginnt der Fluch der Dimensionalität{idx:Fluch der Dimensionalität}.
(d) **Warum der richtige Betrag das Tückische ist.** Man würde erwarten, dass ein falsches
Modell auch einen falschen Kostenbetrag ausgibt — dann fiele es beim Nachrechnen auf. Hier
@ -1855,7 +1855,7 @@ Eignung.
3. Alles, was die Zukunft beeinflusst, und nichts weiter. Unvollständig ist er, wenn die
Kosten oder Übergänge zusätzlich von der Vorgeschichte abhängen.
4. Der Aufwand wächst multiplikativ mit jeder Zustandsdimension. Gegenmittel: gröbere
Diskretisierung/Zustandsreduktion, Funktionsapproximation (ADP), Reinforcement Learning.
Diskretisierung/Zustandsreduktion, Funktionsapproximation (ADP), Reinforcement Learning{idx:Reinforcement Learning}.
5. Weil sie eine **unabhängige** Prüfung ermöglicht — Größenordnungsfehler und
Vorzeichenfehler fallen sofort auf.
@ -1898,7 +1898,7 @@ Wer ihn auflösen will, sollte zuerst prüfen, ob sich die Ressource vermehren l
über Gewichte zu verhandeln.
**{loesung} — Die Front der Relaxation.**
Ohne `integrality` ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig
Ohne `integrality` ist der zulässige Bereich konvex, und die Pareto-Front{idx:Pareto-Front} liegt vollständig
auf ihrer eigenen unteren konvexen Hülle. Es gibt also **keinen** Punkt oberhalb — jeder
Punkt der Front ist durch ein passendes Gewicht erreichbar.
@ -1986,7 +1986,7 @@ aber nicht zu: Auch bei exakt gleichem Budget bliebe der Effekt.
---
## Lösungen zu Kapitel „Predict-then-Optimize“ {#sec:loesungen-prognose}
## Lösungen zu Kapitel „Predict-then-Optimize{idx:Predict-then-Optimize}“ {#sec:loesungen-prognose}
**{loesung} — Andere Preise.**
Der Überhang kostet nur noch 1,50 €, die Fehlmenge weiterhin 6 €. Das kritische Verhältnis
@ -2206,7 +2206,7 @@ der Methode.
1. Montagestunden ↔ Kapital; Plattenmaterial ↔ Risikobudget; Deckungsbeitrag je Stück ↔
erwarteter Ertrag je 1 000 €. (Weitere: Mindestlosgröße ↔ Mindestordergröße,
Rüstkosten ↔ Ordergebühr, Sortimentsbreite ↔ Kardinalitätsgrenze.)
2. Ein LP sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor — Bedeutung kommt
2. Ein LP sieht eine Matrix, einen Kapazitätsvektor{idx:Kapazitätsvektor} und einen Zielvektor — Bedeutung kommt
darin nicht vor. Das ist hier ein Vorteil, weil derselbe geprüfte, getestete Code beide
Domänen bedient; man erbt die Verlässlichkeit mit.
3. „Stellen Sie sich vor, Ihre Engpassmaschine wäre nicht die Fräse, sondern eine Vorschrift:
@ -2506,16 +2506,16 @@ $\sigma_p^2 = 0{,}25\cdot0{,}0625 + 0{,}25\cdot0{,}0144
+ 2\cdot0{,}25\cdot(-0{,}2)\cdot0{,}25\cdot0{,}12
= 0{,}015625 + 0{,}0036 - 0{,}003 = 0{,}016225$, also $\sigma_p = 12{,}74\,\%$.
Beste Sharpe Ratio unter den vier Kandidaten: $(0{,}3;\ 0{,}7)$. Das exakte Optimum liegt
Beste Sharpe Ratio{idx:Sharpe-Ratio} unter den vier Kandidaten: $(0{,}3;\ 0{,}7)$. Das exakte Optimum liegt
bei $w_1 = 0{,}318$. **Bemerkenswert:** Die Mischung $(0{,}3;\ 0{,}7)$ hat mit 10,08 % eine
**geringere** Volatilität als *beide* Einzeltitel (25 % und 12 %) — genau der
**geringere** Volatilität{idx:Volatilität} als *beide* Einzeltitel (25 % und 12 %) — genau der
Diversifikationseffekt, den die negative Korrelation ermöglicht.
**{loesung} — Lambda deuten.** Von $\lambda=0$ (GMV, linker unterer Punkt der Kurve) wandert die
Lösung entlang der Effizienzgrenze nach rechts oben, bis sie bei $\lambda\to\infty$ im Titel
mit der höchsten Rendite endet (bzw. an der Positionsobergrenze).
**{loesung} — Korn-Transformation.**
**{loesung} — Korn-Transformation{idx:Korn-Transformation}.**
$\text{SR}(c\mathbf{w}) = \frac{c\mathbf{w}^\top\boldsymbol{\mu}-r_f}{\sqrt{c^2\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}}$
— bei einem Portfolio mit $\sum w_i = 1$ und Überrenditen geschrieben als
$\mathbf{w}^\top(\boldsymbol{\mu}-r_f\mathbf{1})$ kürzt sich $c$ heraus. Mit der Bedingung
@ -2611,7 +2611,7 @@ aussehen.
**1 — (b).** Das renditestärkste Portfolio auf der Effizienzlinie hat notwendig das
schlechteste Rendite-Risiko-Verhältnis (im Kapitelbeispiel 0,44 gegenüber 0,75 beim
Optimum) und besteht vollständig aus der renditestärksten Einzelanlage — Diversifikation
Optimum) und besteht vollständig aus der renditestärksten Einzelanlage — Diversifikation{idx:Diversifikation}
findet dort gar nicht mehr statt. (a) und (c) sind sachlich falsch: Der Punkt ist numerisch
völlig unproblematisch und wird von CVXPY ohne Weiteres gefunden.
@ -2642,14 +2642,14 @@ Risikowerten, nicht zu Konzentration.
---
## Lösungen zu Kapitel „Tail-Risiko, CVaR und Transaktionskosten“ {#sec:loesungen-cvar}
## Lösungen zu Kapitel „Tail-Risiko, CVaR und Transaktionskosten{idx:Transaktionskosten}“ {#sec:loesungen-cvar}
**{loesung} — VaR und CVaR von Hand.**
Sortierte Verluste: $-2{,}0; -1{,}2; -0{,}8; -0{,}4; 0{,}3; 0{,}6; 1{,}1; 2{,}1; 5{,}4;
8{,}9$. Das 80 %-Quantil ist der 8. Wert: $\text{VaR}_{80\%} = 2{,}1$.
$\text{CVaR}_{80\%} = $ Mittel der schlechtesten 20 % $= (5{,}4+8{,}9)/2 = \mathbf{7{,}15}$.
**{loesung} — Subadditivität.** Ein Risikomaß sollte Diversifikation nie bestrafen: Das Risiko
**{loesung} — Subadditivität{idx:Subadditivität}.** Ein Risikomaß sollte Diversifikation nie bestrafen: Das Risiko
eines zusammengelegten Portfolios darf nicht größer sein als die Summe der Einzelrisiken.
Wird das verletzt, hätte eine Bank einen Anreiz, Portfolios künstlich aufzuspalten, um
Eigenkapitalanforderungen zu senken — ökonomisch unsinnig.
@ -2741,7 +2741,7 @@ wenn man Mengen zusammenlegt; ein Quantil nicht — es kann springen, sobald sic
Reihenfolge der Szenarien ändert.
**Die praktische Konsequenz** ist die Umstellung der Bankenaufsicht mit Basel III vom VaR auf
den *Expected Shortfall* — dieselbe Größe, die hier CVaR heißt. Und für Ihre eigenen
den *Expected Shortfall*{idx:Expected Shortfall} — dieselbe Größe, die hier CVaR heißt. Und für Ihre eigenen
Modelle, auch außerhalb der Finanzwelt: Wo immer Sie eine Risikokennzahl über Einheiten
aggregieren wollen, prüfen Sie zuerst, ob das Maß das überhaupt zulässt.
@ -2785,7 +2785,7 @@ Anleihen dort sind **gerade unabhängig**, und genau deshalb tritt die Verletzun
**{loesung} — Lookahead erkennen.**
(a) sauber (`:heute`). (b) **Lookahead** — gesamter Zeitraum. (c) **Lookahead**
Volatilität über den ganzen Zeitraum enthält Zukunft. (d) sauber — Gewichte aus Daten bis
heute, angewendet auf heute (idealerweise auf morgen). (e) **Survivorship-Bias** — das
heute, angewendet auf heute (idealerweise auf morgen). (e) **Survivorship-Bias**{idx:Survivorship-Bias} — das
Universum wird danach gefiltert, wer über den gesamten Zeitraum Daten hat.
**{loesung} — Kennzahlen deuten.**
@ -2795,7 +2795,7 @@ B: Sharpe $= (8-2)/9 = 0{,}67$, Calmar $= 8/12 = 0{,}67$.
Auszahlungen in einer Verlustphase Substanz vernichten. B ist in beiden risikoadjustierten
Maßen besser.
**{loesung} — Rebalancing-Kalender.** Etwa 2530 % der Termine fallen aus. Die Kennzahlen ändern
**{loesung} — Rebalancing{idx:Rebalancing}-Kalender.** Etwa 2530 % der Termine fallen aus. Die Kennzahlen ändern
sich messbar — in welche Richtung, ist zufällig. Genau das ist der Punkt: Der Fehler
verzerrt, ohne aufzufallen.
@ -2808,7 +2808,7 @@ Gebühren ist monatlich meist vertretbar, bei 0,5 % eher quartalsweise. Ein **To
wenigen Perioden. Folgerung: Eine gute Gesamtkennzahl kann von einer einzigen glücklichen
Phase getragen sein — **immer** nach Teilzeiträumen aufschlüsseln.
**{loesung} — Data Snooping messen.** Erwartetes Ergebnis: Die beste In-Sample-Sharpe-Ratio liegt
**{loesung} — Data Snooping{idx:Data Snooping} messen.** Erwartetes Ergebnis: Die beste In-Sample-Sharpe-Ratio liegt
deutlich über dem Mittelwert aller Varianten; auf dem zurückgehaltenen Zeitraum fällt sie
Richtung Mittelwert zurück. Die **Differenz zwischen (a) und (c) ist der Selektionseffekt**
— genau das, was die Deflated Sharpe Ratio korrigieren soll.
@ -2927,7 +2927,7 @@ Wochenendverteilung.
(d) 3060 s, Gap 1 % — die Datenunsicherheit ist ohnehin größer.
**{loesung} — Relaxation einbauen.** Muster wie in `Infeasibility_Diagnose.py`:
Schlupfvariable je Mindestbesetzung, Strafe deutlich über allen weichen Zielen, aber
Schlupfvariable{idx:Schlupfvariable} je Mindestbesetzung, Strafe deutlich über allen weichen Zielen, aber
endlich. Wichtig: **gestaffelte** Strafen, damit der Solver die *am wenigsten schmerzhafte*
Verletzung wählt.
@ -3039,7 +3039,7 @@ Zielfunktion bepreist ist, sagt nichts darüber, ob sie eingehalten wurde.
**3 — (c) hierarchisch lockern.** `INFEASIBLE` sagt nur, *dass* ein Widerspruch existiert,
nicht *welche* Regeln ihn erzeugen. Ersetzt man harte Verbote durch sehr teure Strafkosten,
liefert der Solver wieder eine Lösung — und die verletzten Regeln stehen benannt in der
Kostenzerlegung. Genau das tut `Infeasibility_Diagnose.py`. (a) hilft nicht: `INFEASIBLE`
Kostenzerlegung{idx:Kostenzerlegung}. Genau das tut `Infeasibility_Diagnose.py`. (a) hilft nicht: `INFEASIBLE`
ist ein Beweis, kein Abbruch. (b) verschiebt das Problem und verliert die Gelegenheit, die
Ursache zu finden, solange sie noch frisch ist.

Some files were not shown because too many files have changed in this diff Show more