<aclass="brand"href="index.html"><svgclass="icon"aria-hidden="true"><usehref="#icon-book"></use></svg><span>Optimierte Entscheidungsfindung mit Python</span></a>
<navclass="sidebar"id="sidebar"aria-label="Kapitelnavigation"><divclass="sidebar-inhalt"><detailsclass="sidebar-gruppe"><summary>Einstieg</summary><ul><lidata-kapitel="vorwort.html"><ahref="vorwort.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Vorwort & Lesehilfe</span></a></li><lidata-kapitel="notation.html"><ahref="notation.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Notation & Abkürzungen</span></a></li></ul></details><detailsclass="sidebar-gruppe"><summary>Teil I: Grundlagen des Operations Research</summary><ul><lidata-kapitel="einfuehrung.html"><ahref="einfuehrung.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</span></a></li><lidata-kapitel="fundament.html"><ahref="fundament.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität</span></a></li><lidata-kapitel="oekosystem.html"><ahref="oekosystem.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a></li><lidata-kapitel="modellierung.html"><ahref="modellierung.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 4: Vom Management-Wunsch zum Modell</span></a></li><lidata-kapitel="synthese-grundlagen.html"><ahref="synthese-grundlagen.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Synthese Teil I</span></a></li></ul></details><detailsclass="sidebar-gruppe"><summary>Teil II: Die Kernverfahren der deterministischen Optimierung</summary><ul><lidata-kapitel="lp.html"><ahref="lp.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise</span></a></li><lidata-kapitel="milp.html"><ahref="milp.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound</span></a></li><lidata-kapitel="cpsat.html"><ahref="cpsat.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung</span></a></li><lidata-kapitel="graphen.html"><ahref="graphen.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP</span></a></li><lidata-kapitel="metaheuristiken.html"><ahref="metaheuristiken.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a></li><lidata-kapitel="dekomposition.html"><ahref="dekomposition.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></a></li><lidata-kapitel="synthese-kernverfahren.html"><ahref="synthese-kernverfahren.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Synthese Teil II</span></a></li></ul></details><detailsclass="sidebar-gruppe"open><summary>Teil III: Nichtlinearität, Unsicherheit und mehrperiod
<h1id="teil-nichtlinear">Teil III: Nichtlinearität, Unsicherheit und mehrperiodige Dynamik</h1>
<p><ahref="lp.html#teil-kernverfahren">Teil II</a> ging von festen Daten und linearen Zusammenhängen aus. Beides gilt in der Praxis oft nicht: Risiko wächst <strong>quadratisch</strong> mit dem Einsatz, Nachfrage ist erst morgen bekannt, und eine Entscheidung heute verändert, welche Möglichkeiten übermorgen noch offenstehen.</p>
<p>Drei Kapitel dieses Teils behandeln genau diese drei Abweichungen. Welches Werkzeug Sie brauchen, hängt davon ab, <strong>welche</strong> davon bei Ihnen vorliegt:</p>
<imgsrc="bilder_04/teil3_solverwahl.svg"alt="Entscheidungsdiagramm: Welches Werkzeug passt zu meinem Problem? (Teil III)"/>
<figcaptionaria-hidden="true">Entscheidungsdiagramm: Welches Werkzeug passt zu meinem Problem? (Teil III)</figcaption>
</figure>
<blockquote>
<p><strong>🎯 Merksatz zum Diagramm</strong> Die erste Frage trennt zwei Welten, die man nicht vermischen darf. Ist die <strong>Nichtlinearität</strong> das Problem, geht es um Konvexität — und damit darum, ob Sie eine Optimalitätsgarantie bekommen oder nur ein lokales Optimum. Ist die <strong>Unsicherheit</strong> 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.</p>
<p>Der häufigste Fehler an dieser Stelle ist, Unsicherheit durch <strong>Mittelwerte</strong> zu ersetzen und dann deterministisch zu rechnen. Warum das systematisch danebengeht, zeigt <ahref="unsicherheit.html#kap-unsicherheit">Kapitel 12</a> unter dem Namen <em>Fluch des Durchschnitts</em>.</p>
<p>Zwei weitere Kapitel schließen sich an, weil sie dieselbe Voraussetzung aufgeben, nur an einer anderen Stelle: <ahref="mehrziel.html#kap-mehrziel">Kapitel 14</a> gibt das <strong>eine Ziel</strong> auf, <ahref="prognose.html#kap-prognose">Kapitel 15</a> die Annahme, die <strong>Eingabedaten</strong> seien gegeben statt selbst geschätzt. Was dieser Teil insgesamt leistet, fasst die Synthese an seinem Ende zusammen.</p>
<h1id="kap-qp-nlp">Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität</h1>
<divclass="card card-blick">
<blockquote>
<p><strong>📌 Kapitel auf einen Blick</strong></p>
<p><strong>Worum geht es?</strong> 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 Portfoliooptimierung in <ahref="bruecke.html#teil-anwendungen">Teil IV</a>.</p>
<p><strong>Voraussetzungen:</strong><ahref="fundament.html#kap-fundament">Kapitel 2</a> (Konvexität, Eigenwerte), <ahref="lp.html#kap-lp">Kapitel 5</a> (Dualität, Schattenpreise). Gradienten werden in <ahref="#sec:qp-nlp-lernziele">Abschnitt 11.2</a> wiederholt.</p>
<p><strong>Danach können Sie:</strong> 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, ohne ein lokales Optimum für das Optimum zu halten.</p>
<p><strong>Zeitbedarf:</strong> ca. 6,5 Stunden.</p>
<p>Drei Dinge stehen in dieser Tabelle, und alle drei widersprechen der Intuition:</p>
<oltype="1">
<li><strong>Die Beimischung der <em>riskanteren</em> Anlage senkt das Risiko</strong> — von 12 % auf 8,9 %, also um mehr als ein Viertel. Wer nur auf die 28 % schaut, würde B nie anfassen.</li>
<li><strong>Naives Streuen macht es schlechter.</strong> Die 50/50-Mischung liegt mit 12,84 % <em>über</em> dem Wert, den man bekäme, wenn man ausschließlich A hielte. „Diversifizieren“ ist also keine Regel, sondern eine Frage nach dem richtigen Maß.</li>
<li><strong>Das richtige Maß ist weder 0 % noch 50 %</strong>, sondern 23,3 % — eine Zahl, auf die man durch Nachdenken nicht kommt. Genau dafür gibt es dieses Kapitel.</li>
</ol>
<p><strong>Warum funktioniert das?</strong> Weil Risiko sich nicht addiert. Die Schwankung einer Mischung ist nicht der Durchschnitt der Einzelschwankungen, sondern folgt einer <strong>quadratischen</strong> Form:</p>
<p>Der letzte Term ist der entscheidende: Bei negativer Korrelation ist er <strong>negativ</strong> und zieht das Gesamtrisiko herunter. Deshalb verlässt man mit Risiko die Welt der Polyeder aus <ahref="lp.html#teil-kernverfahren">Teil II</a> — die Zielfunktion ist kein Skalarprodukt mehr, sondern eine quadratische Form. Was das für die Lösbarkeit bedeutet und warum CVXPY das trotzdem mit Optimalitätsgarantie löst, klärt der Rest des Kapitels.</p>
<blockquote>
<p><strong>🎯 Merksatz</strong><code>cp.quad_form(w, S)</code> ist die Zeile, um die es im ganzen Kapitel geht. Sie ist genau dann harmlos, wenn <spanclass="math inline">\mathbf{S}</span><strong>positiv semidefinit</strong> ist — und genau dann ein Problem, wenn nicht. CVXPY prüft das für Sie und verweigert die Arbeit im Zweifelsfall. Diese Verweigerung ist ein Schutzmechanismus, keine Schikane.</p>
</blockquote>
<hr/>
<h2id="sec:qp-nlp-lernziele">11.2 Lernziele</h2>
<p>Nach diesem Kapitel können Sie …</p>
<oltype="1">
<li>… ein <strong>quadratisches Programm (QP)</strong> aufstellen und von einem allgemeinen nichtlinearen Programm abgrenzen.</li>
<li>… die <strong>KKT-Bedingungen</strong> herleiten und auf ein Optimierungsproblem anwenden.</li>
<li>… den Zusammenhang zwischen <strong>Lagrange-Multiplikator</strong> und <strong>Schattenpreis</strong> erklären.</li>
<li>… zwischen <strong>konvex</strong> und <strong>streng konvex</strong> unterscheiden und eine gültige, positiv semidefinite <strong>Kovarianzmatrix</strong> konstruieren.</li>
</ol>
<h3id="auffrischung-gradient-in-einer-minute">Auffrischung: Gradient in einer Minute</h3>
<p>Der <strong>Gradient</strong><spanclass="math inline">\nabla f(\mathbf{x})</span> ist der Vektor aller partiellen Ableitungen:</p>
<p><strong>📐 Formel-Lesehilfe</strong><spanclass="math inline">\partial f / \partial x_1</span> bedeutet: „Wie stark ändert sich <spanclass="math inline">f</span>, wenn ich <strong>nur</strong><spanclass="math inline">x_1</span> ein winziges Stück erhöhe und alles andere festhalte?“</p>
<p><strong>Ohne Formel gesagt:</strong> Der Gradient ist der Pfeil, der in die Richtung des steilsten <strong>Anstiegs</strong> zeigt. Seine Länge ist die Steilheit. Beim Minimieren geht man deshalb in Richtung <spanclass="math inline">-\nabla f</span> — bergab.</p>
<p><strong>Die eine Regel, die Sie brauchen:</strong> Im Inneren eines Gebiets ist an einem Minimum der Gradient <strong>null</strong> — es geht nirgendwohin mehr bergab. An einem Rand (also wenn eine Nebenbedingung bindet) gilt das nicht mehr, und genau dafür braucht man die KKT-Bedingungen.</p>
</blockquote>
<p>Zwei Ableitungsregeln, die im ganzen Buch gebraucht werden:</p>
\qquad\text{u. d. N.}\qquad \mathbf{A}\mathbf{x}\le\mathbf{b},\quad \mathbf{F}\mathbf{x}=\mathbf{d}
</span></p>
<blockquote>
<p><strong>📐 Formel-Lesehilfe</strong> * <spanclass="math inline">\mathbf{x}^\top\mathbf{P}\mathbf{x}</span> — die <strong>quadratische Form</strong>. Ausgeschrieben: <spanclass="math inline">\sum_i\sum_j p_{ij}x_ix_j</span>. In der Portfoliotheorie ist das exakt die Portfoliovarianz, wenn <spanclass="math inline">\mathbf{P} = \boldsymbol{\Sigma}</span> die Kovarianzmatrix ist. * Der Faktor <spanclass="math inline">\tfrac12</span> ist reine Bequemlichkeit: Er kürzt sich beim Ableiten weg (<spanclass="math inline">\nabla = \mathbf{P}\mathbf{x}</span> statt <spanclass="math inline">2\mathbf{P}\mathbf{x}</span>). * <spanclass="math inline">\mathbf{q}^\top\mathbf{x}</span> — ein zusätzlicher linearer Teil, etwa die negative erwartete Rendite.</p>
<p><strong>Ohne Formel gesagt:</strong> „Minimiere das Risiko (quadratisch) abzüglich des Ertrags (linear), unter Einhaltung der Budget- und Positionsgrenzen.“</p>
</blockquote>
<h3id="konvexität-die-präzise-aussage">Konvexität: die präzise Aussage</h3>
<p>Eine verbreitete, aber zu stark formulierte Aussage lautet: <em>„Wenn <spanclass="math inline">\mathbf{P}</span> positiv semi-definit ist, ist das QP streng konvex; es existiert ein eindeutiges globales Minimum.“</em> Korrekt gilt:</p>
<td>Die Funktion ist eine „Schüssel“, die eine flache Rinne haben darf</td>
<td>Problem ist <strong>konvex</strong>. Jedes lokale Minimum ist global. Es kann aber <strong>mehrere Minimalstellen</strong> geben — mit demselben Zielwert</td>
<td>Problem ist <strong>streng konvex</strong>. Die Minimalstelle ist <strong>eindeutig</strong></td>
</tr>
<trclass="odd">
<td><spanclass="math inline">\mathbf{P}</span> hat einen negativen Eigenwert</td>
<td>Sattel oder Rinne nach unten</td>
<td>Problem ist <strong>nicht konvex</strong>. Lokale Optima möglich, keine Garantie</td>
</tr>
</tbody>
</table>
<p><strong>Warum das praktisch zählt:</strong> Eine Stichproben-Kovarianzmatrix mit mehr Titeln als Beobachtungen (<spanclass="math inline">N > T</span>) ist <strong>singulär</strong> — 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 dieses Problem behebt die Shrinkage aus <ahref="finanzdaten.html#kap-finanzdaten">Kapitel 18</a>.</p>
<p>Alle drei Zeilen der Tabelle lassen sich an einem winzigen Zwei-Variablen-QP direkt beobachten — inklusive der Stelle, an der CVXPY ein nicht konvexes Problem <strong>verweigert</strong>, bevor überhaupt ein Solver aufgerufen wird:</p>
<spanid="cb3-5"><ahref="#cb3-5"aria-hidden="true"tabindex="-1"></a><spanclass="co">Kapitel QP/NLP: Die drei Faelle aus der Konvexitaets-Tabelle (Abschnitt</span></span>
<spanid="cb3-7"><ahref="#cb3-7"aria-hidden="true"tabindex="-1"></a><spanclass="co">einem Mini-QP demonstriert: P positiv definit, P (singulaer) semidefinit,</span></span>
<spanid="cb3-8"><ahref="#cb3-8"aria-hidden="true"tabindex="-1"></a><spanclass="co">P mit negativem Eigenwert.</span></span>
<spanid="cb3-24"><ahref="#cb3-24"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Eigenwerte von P: </span><spanclass="sc">{</span>np<spanclass="sc">.</span><spanclass="bu">round</span>(eigenwerte, <spanclass="dv">4</span>)<spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb3-28"><ahref="#cb3-28"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"-> CVXPY lehnt das Problem ab, BEVOR ueberhaupt ein Solver laeuft."</span>)</span>
<spanid="cb3-40"><ahref="#cb3-40"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Fall 1: P positiv definit -> eindeutiges Minimum</span></span>
<spanid="cb3-44"><ahref="#cb3-44"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Fall 2: P singulaer/semidefinit (zwei "identische" Assets) -> unendlich viele Minima</span></span>
<spanid="cb3-48"><ahref="#cb3-48"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Fall 3: P mit negativem Eigenwert -> nicht konvex</span></span>
<spanid="cb3-52"><ahref="#cb3-52"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st">--- Nachweis: 'unendlich viele Minima' im semidefiniten Fall ---"</span>)</span>
<spanid="cb3-53"><ahref="#cb3-53"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> punkt <spanclass="kw">in</span> [np.array([<spanclass="fl">1.0</span>, <spanclass="fl">0.0</span>]), np.array([<spanclass="fl">0.0</span>, <spanclass="fl">1.0</span>]), np.array([<spanclass="fl">0.3</span>, <spanclass="fl">0.7</span>])]:</span>
<spanid="cb3-54"><ahref="#cb3-54"aria-hidden="true"tabindex="-1"></a> wert <spanclass="op">=</span><spanclass="fl">0.5</span><spanclass="op">*</span> punkt <spanclass="op">@</span> P_semidefinit <spanclass="op">@</span> punkt</span>
<spanid="cb3-55"><ahref="#cb3-55"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" w = </span><spanclass="sc">{</span>punkt<spanclass="sc">}</span><spanclass="ss"> -> Zielwert = </span><spanclass="sc">{</span>wert<spanclass="sc">:.4f}</span><spanclass="ss"> (identisch, obwohl w verschieden)"</span>)</span></code></pre></div>
<p><strong>Erwartete Ausgabe:</strong></p>
<pre><code>--- P positiv definit ---
Eigenwerte von P: [0.7929 2.2071]
DCP-konvex (CVXPY-Pruefung)? True
Status: optimal
w* = [0.25 0.75]
Zielwert = 0.437500
--- P positiv semidefinit (singulaer) ---
Eigenwerte von P: [0. 2.]
DCP-konvex (CVXPY-Pruefung)? True
Status: optimal
w* = [0.5 0.5]
Zielwert = 0.500000
--- P indefinit (negativer Eigenwert) ---
Eigenwerte von P: [-1. 3.]
DCP-konvex (CVXPY-Pruefung)? False
-> CVXPY lehnt das Problem ab, BEVOR ueberhaupt ein Solver laeuft.
--- Nachweis: 'unendlich viele Minima' im semidefiniten Fall ---
w = [1. 0.] -> Zielwert = 0.5000 (identisch, obwohl w verschieden)
w = [0. 1.] -> Zielwert = 0.5000 (identisch, obwohl w verschieden)
w = [0.3 0.7] -> Zielwert = 0.5000 (identisch, obwohl w verschieden)</code></pre>
<p>Der letzte Block ist der eigentliche Beweis: Drei völlig verschiedene Portfolios <spanclass="math inline">(1,0)</span>, <spanclass="math inline">(0,1)</span> und <spanclass="math inline">(0{,}3;\,0{,}7)</span> liefern <strong>exakt</strong> denselben Zielwert — die „Rinne” aus der Tabelle ist keine Metapher, sie ist hier eine echte Gerade im Lösungsraum.</p>
<blockquote>
<p><strong>⚠️ Prüfen Sie jede Matrix, bevor Sie sie verwenden</strong></p>
<spanid="cb5-2"><ahref="#cb5-2"aria-hidden="true"tabindex="-1"></a><spanclass="cf">assert</span> np.<spanclass="bu">all</span>(eigenwerte <spanclass="op">>=</span><spanclass="op">-</span><spanclass="fl">1e-10</span>), <spanclass="st">"Sigma ist nicht positiv semidefinit!"</span></span>
<p>Eine einzige Zeile — und sie deckt eine ungültige Kovarianzmatrix sofort auf.</p>
</blockquote>
<hr/>
<h2id="sec:qp-nlp-die-karush-kuhn-tucker-bedingungen">11.4 Die Karush-Kuhn-Tucker-Bedingungen</h2>
<p>Die <strong>KKT-Bedingungen</strong> verallgemeinern den Lagrange-Ansatz auf Probleme mit <strong>Ungleichungen</strong>. Sie sind notwendig für ein Optimum — und bei konvexen Problemen auch hinreichend.</p>
<p>Für das Problem</p>
<p><spanclass="math display">
\min f(\mathbf{x}) \quad\text{u. d. N.}\quad g_i(\mathbf{x})\le0\ (i=1..m),\qquad h_j(\mathbf{x})=0\ (j=1..p)
</span></p>
<p>lautet die <strong>Lagrange-Funktion</strong>:</p>
<p><strong>📐 Formel-Lesehilfe</strong> Die Lagrange-Funktion <strong>verpackt die Nebenbedingungen in die Zielfunktion</strong>. Jede Bedingung bekommt einen Preis: <spanclass="math inline">\lambda_i</span> bzw. <spanclass="math inline">\nu_j</span>. Verletzt man Bedingung <spanclass="math inline">i</span>, wird <spanclass="math inline">g_i > 0</span>, und der Term <spanclass="math inline">\lambda_i g_i</span> erhöht <spanclass="math inline">\mathcal{L}</span> — es kostet also.</p>
<p><strong>Ohne Formel gesagt:</strong> Statt „du darfst nicht“ sagt man „du darfst, aber es kostet <spanclass="math inline">\lambda_i</span> je Einheit Überschreitung“. Bei den richtigen Preisen verhält sich der Optimierer dann von selbst regelkonform. Diese Preise sind exakt die <strong>Schattenpreise</strong> aus <ahref="lp.html#kap-lp">Kapitel 5</a>.</p>
</blockquote>
<h3id="die-vier-kkt-bedingungen">Die vier KKT-Bedingungen</h3>
<p><em>Anschaulich:</em> Die Kraft, die den Punkt bergab ziehen will (<spanclass="math inline">-\nabla f</span>), wird exakt von den Nebenbedingungen aufgefangen. Wie ein Ball, der in einer Ecke liegen bleibt: Die Schwerkraft zieht, die Wände drücken dagegen, und die Summe ist null.</p>
<p><strong>3. Duale Zulässigkeit</strong> — die Multiplikatoren der Ungleichungen sind nichtnegativ: <spanclass="math inline">\lambda_i^*\ge0</span>.</p>
<p><em>Warum?</em> Eine Wand kann nur <strong>drücken</strong>, nicht ziehen. Ein negatives <spanclass="math inline">\lambda</span> hieße, die Bedingung würde die Lösung von sich weg<strong>ziehen</strong> — dann wäre sie nicht bindend.</p>
<p><strong>4. Komplementärer Schlupf</strong> — <spanclass="math inline">\lambda_i^*\cdot g_i(\mathbf{x}^*)=0</span> für alle <spanclass="math inline">i</span>.</p>
<p><em>Bedeutung:</em> Entweder ist die Bedingung nicht bindend (<spanclass="math inline">g_i<0</span>), <strong>dann muss</strong><spanclass="math inline">\lambda_i=0</span> sein. Oder der Multiplikator ist positiv, <strong>dann muss</strong> die Bedingung mit Gleichheit binden. Das ist derselbe Satz wie in <ahref="lp.html#kap-lp">Kapitel 5</a> — nur allgemeiner formuliert.</p>
<figure>
<imgsrc="bilder_04/kap_qp_kkt.svg"alt="Abb. 11.1: Links die Handrechnung dieses Abschnitts: Das freie Minimum liegt bei x=5, die Grenze lässt nur x \le 3 zu, und \lambda^* = 4 ist genau die Steigung von f am Rand — der Preis dafür, dass es nicht weitergeht. Rechts dieselbe Aussage in zwei Dimensionen: \nabla f und \lambda^*\nabla g zeigen gegeneinander und heben sich auf. Das Skript bilder_04/erzeuge_kkt.py prüft alle vier KKT-Bedingungen numerisch, bevor es zeichnet."/>
<figcaptionaria-hidden="true">Abb. 11.1: Links die Handrechnung dieses Abschnitts: Das freie Minimum liegt bei <spanclass="math inline">x=5</span>, die Grenze lässt nur <spanclass="math inline">x \le 3</span> zu, und <spanclass="math inline">\lambda^* = 4</span> ist genau die Steigung von <spanclass="math inline">f</span> am Rand — der Preis dafür, dass es nicht weitergeht. Rechts dieselbe Aussage in zwei Dimensionen: <spanclass="math inline">\nabla f</span> und <spanclass="math inline">\lambda^*\nabla g</span> zeigen gegeneinander und heben sich auf. Das Skript <code>bilder_04/erzeuge_kkt.py</code> prüft alle vier KKT-Bedingungen numerisch, bevor es zeichnet.</figcaption>
</figure>
<divclass="card card-formel">
<blockquote>
<p><strong>🔤 Formel-Übersetzer: die vier KKT-Bedingungen</strong></p>
<p>Vier Zeilen Mathematik, vier Sätze, die man einem Betriebsleiter sagen könnte:</p>
<td>Der <strong>Schattenpreis</strong> von Bedingung <spanclass="math inline">i</span> — dieselbe Größe wie im LP (<ahref="lp.html#kap-lp">Kapitel 5</a>), nur für krumme Ränder.</td>
</tr>
</tbody>
</table>
<p><strong>Alle vier zusammen in einem Satz:</strong><em>Wir stehen an einer Stelle, an der jede Verbesserung an eine Grenze stößt — und wir wissen für jede dieser Grenzen, was ihre Lockerung wert wäre.</em></p>
<p><strong>Warum das praktisch zählt:</strong> Die KKT-Bedingungen sind nicht nur Theorie, sondern eine <strong>Prüfvorschrift</strong>. Bei einem konvexen Problem sind sie notwendig <em>und</em> hinreichend — wer sie an einer gefundenen Lösung nachrechnet, hat damit bewiesen, dass sie optimal ist. Genau das tut <code>KKT_Nachweis.py</code> weiter unten.</p>
</blockquote>
</div>
<blockquote>
<p><strong>✏️ Handrechnung 11.1: KKT an einem Minimalbeispiel</strong></p>
<p><spanclass="math display">\min\ f(x) = (x-5)^2 \qquad\text{u. d. N.}\qquad g(x) = x - 3 \le 0</span></p>
<p><strong>Ohne Nebenbedingung</strong> läge das Minimum bei <spanclass="math inline">x = 5</span>. Aber <spanclass="math inline">5 > 3</span> — unzulässig.</p>
<p><strong>Und was bedeutet <spanclass="math inline">\lambda^* = 4</span>?</strong> Es ist der Schattenpreis: Dürfte <spanclass="math inline">x</span> bis 3,1 gehen, sänke <spanclass="math inline">f</span> um etwa <spanclass="math inline">4 \times 0{,}1 = 0{,}4</span>. Probe: <spanclass="math inline">f(3{,}1) = (3{,}1-5)^2 = 3{,}61</span>, tatsächlicher Rückgang <spanclass="math inline">0{,}39</span> ✓ — die kleine Abweichung ist die Krümmung.</p>
<spanid="cb6-7"><ahref="#cb6-7"aria-hidden="true"tabindex="-1"></a><spanclass="co">Loest ein QP mit CVXPY, liest die Dualwerte aus und prueft alle vier</span></span>
<spanid="cb6-8"><ahref="#cb6-8"aria-hidden="true"tabindex="-1"></a><spanclass="co">KKT-Bedingungen einzeln nach. Das ist zugleich eine Vorlage fuer die</span></span>
<spanid="cb6-18"><ahref="#cb6-18"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Baut Sigma = D * C * D aus Volatilitaeten und einer Korrelationsmatrix.</span></span>
<spanid="cb6-19"><ahref="#cb6-19"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Dieses Vorgehen ist konstruktionsbedingt positiv semidefinit - im</span></span>
<spanid="cb6-20"><ahref="#cb6-20"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Gegensatz zum nachtraeglichen Ueberschreiben der Diagonalen, das die</span></span>
<spanid="cb6-23"><ahref="#cb6-23"aria-hidden="true"tabindex="-1"></a> C <spanclass="op">=</span> np.array(korrelationen, dtype<spanclass="op">=</span><spanclass="bu">float</span>)</span>
<spanid="cb6-24"><ahref="#cb6-24"aria-hidden="true"tabindex="-1"></a><spanclass="cf">assert</span> np.allclose(C, C.T), <spanclass="st">"Korrelationsmatrix muss symmetrisch sein."</span></span>
<spanid="cb6-25"><ahref="#cb6-25"aria-hidden="true"tabindex="-1"></a><spanclass="cf">assert</span> np.allclose(np.diag(C), <spanclass="fl">1.0</span>), <spanclass="st">"Diagonale der Korrelationsmatrix muss 1 sein."</span></span>
<spanid="cb6-26"><ahref="#cb6-26"aria-hidden="true"tabindex="-1"></a> eigen <spanclass="op">=</span> np.linalg.eigvalsh(C)</span>
<spanid="cb6-28"><ahref="#cb6-28"aria-hidden="true"tabindex="-1"></a><spanclass="ss">f"Korrelationsmatrix ist nicht positiv semidefinit "</span></span>
<spanid="cb6-29"><ahref="#cb6-29"aria-hidden="true"tabindex="-1"></a><spanclass="ss">f"(kleinster Eigenwert </span><spanclass="sc">{</span>eigen<spanclass="sc">.</span><spanclass="bu">min</span>()<spanclass="sc">:.4f}</span><spanclass="ss">). Solche Korrelationen sind unmoeglich."</span>)</span>
<spanid="cb6-30"><ahref="#cb6-30"aria-hidden="true"tabindex="-1"></a> D <spanclass="op">=</span> np.diag(vola)</span>
<spanid="cb6-31"><ahref="#cb6-31"aria-hidden="true"tabindex="-1"></a><spanclass="cf">return</span> D <spanclass="op">@</span> C <spanclass="op">@</span> D</span>
<spanid="cb6-46"><ahref="#cb6-46"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" KKT-BEDINGUNGEN AM PORTFOLIO-QP"</span>)</span>
<spanid="cb6-48"><ahref="#cb6-48"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Eigenwerte von Sigma: </span><spanclass="sc">{</span>np<spanclass="sc">.</span><spanclass="bu">round</span>(eigenwerte, <spanclass="dv">6</span>)<spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb6-58"><ahref="#cb6-58"aria-hidden="true"tabindex="-1"></a> nichtnegativ <spanclass="op">=</span> w <spanclass="op">>=</span><spanclass="dv">0</span></span>
<spanid="cb6-59"><ahref="#cb6-59"aria-hidden="true"tabindex="-1"></a> problem <spanclass="op">=</span> cp.Problem(ziel, [budget, nichtnegativ])</span>
<spanid="cb6-63"><ahref="#cb6-63"aria-hidden="true"tabindex="-1"></a> nu <spanclass="op">=</span> budget.dual_value <spanclass="co"># Multiplikator der Gleichung</span></span>
<spanid="cb6-64"><ahref="#cb6-64"aria-hidden="true"tabindex="-1"></a> lam_i <spanclass="op">=</span> nichtnegativ.dual_value <spanclass="co"># Multiplikatoren der Ungleichungen</span></span>
<spanid="cb6-69"><ahref="#cb6-69"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Multiplikator der Budgetgleichung (nu): </span><spanclass="sc">{</span>nu<spanclass="sc">:.6f}</span><spanclass="ss">"</span>)</span>
<spanid="cb6-70"><ahref="#cb6-70"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Multiplikatoren der w>=0-Bedingungen: </span><spanclass="sc">{</span>np<spanclass="sc">.</span><spanclass="bu">round</span>(lam_i, <spanclass="dv">6</span>)<spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb6-73"><ahref="#cb6-73"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st">--- Pruefung der vier KKT-Bedingungen ---"</span>)</span>
<spanid="cb6-75"><ahref="#cb6-75"aria-hidden="true"tabindex="-1"></a><spanclass="co"># 1. Stationaritaet: grad f - lambda + nu*1 = 0</span></span>
<spanid="cb6-76"><ahref="#cb6-76"aria-hidden="true"tabindex="-1"></a><spanclass="co"># f(w) = lam/2 w'Sigma w - mu'w -> grad f = lam*Sigma w - mu</span></span>
<spanid="cb6-99"><ahref="#cb6-99"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss">Alle vier KKT-Bedingungen erfuellt: </span><spanclass="sc">{</span>alle_ok<spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb6-101"><ahref="#cb6-101"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- Interpretation von nu -------------------------------------------</span></span>
<spanid="cb6-102"><ahref="#cb6-102"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st">--- Was bedeutet nu? ---"</span>)</span>
<spanid="cb6-103"><ahref="#cb6-103"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"nu ist der Schattenpreis des Budgets: Um so viel aendert sich der"</span>)</span>
<spanid="cb6-104"><ahref="#cb6-104"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Zielwert, wenn man statt 100 % nur 99 </span><spanclass="sc">% i</span><spanclass="st">nvestieren duerfte."</span>)</span>
<spanid="cb6-106"><ahref="#cb6-106"aria-hidden="true"tabindex="-1"></a> [cp.<spanclass="bu">sum</span>(w) <spanclass="op">==</span><spanclass="fl">1.01</span>, w <spanclass="op">>=</span><spanclass="dv">0</span>])</span>
<h2id="sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize">11.5 Nichtlineare Optimierung mit <code>scipy.optimize.minimize</code></h2>
<p>Wenn Zielfunktion oder Nebenbedingungen weder linear noch quadratisch sind — Logarithmen, Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie <strong>SLSQP</strong> (<em>Sequential Least Squares Programming</em>).</p>
<blockquote>
<p><strong>⚠️ Der entscheidende Unterschied zu CVXPY</strong> CVXPY <strong>prüft</strong> die Konvexität und garantiert bei Erfolg das globale Optimum. <code>scipy.optimize.minimize</code> prüft nichts und liefert ein <strong>lokales</strong> Optimum, das vom Startpunkt abhängt (siehe die Demonstration in <ahref="fundament.html#sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und">Abschnitt 2.5</a>). Wer SLSQP einsetzt, sollte <strong>immer mit mehreren Startpunkten rechnen</strong> und die Ergebnisse vergleichen.</p>
<p>Wir optimieren ein Portfolio nicht nur nach Varianz, sondern maximieren zusätzlich die <strong>Shannon-Entropie</strong> der Gewichte, um Klumpenrisiken glatt zu bestrafen:</p>
<p><strong>📐 Formel-Lesehilfe zur Entropie</strong><spanclass="math inline">H(\mathbf{w}) = -\sum_i w_i \ln w_i</span> ist ein Streuungsmaß: * Alles in einem Titel (<spanclass="math inline">w = (1,0,0,0)</span>): <spanclass="math inline">H = 0</span> — minimale Streuung. * Gleichverteilt (<spanclass="math inline">w_i = 1/4</span>): <spanclass="math inline">H = \ln 4 \approx 1{,}386</span> — maximale Streuung.</p>
<p><strong>Ohne Formel gesagt:</strong> Die Entropie misst, wie „breit verteilt“ das Portfolio ist. Weil sie mit einem Minuszeichen in die zu minimierende Zielfunktion eingeht, wird breite Streuung <strong>belohnt</strong> — und zwar mathematisch glatt, ohne harte Obergrenzen. Der Vorteil gegenüber einer Schranke wie <spanclass="math inline">w_i \le 0{,}2</span>: Der Übergang ist weich, das Ergebnis reagiert nicht sprunghaft auf kleine Datenänderungen.</p>
<spanid="cb7-5"><ahref="#cb7-5"aria-hidden="true"tabindex="-1"></a><spanclass="co">Kapitel QP/NLP: Nichtlineare Optimierung (NLP) mit scipy SLSQP.</span></span>
<spanid="cb7-6"><ahref="#cb7-6"aria-hidden="true"tabindex="-1"></a><spanclass="co">Modell: Mean-Variance-Portfolio mit Shannon-Entropie-Diversifikation.</span></span>
<spanid="cb7-8"><ahref="#cb7-8"aria-hidden="true"tabindex="-1"></a><spanclass="co">Achtung, haeufiger Fehler: Sigma per A@A.T zu erzeugen und anschliessend die</span></span>
<spanid="cb7-9"><ahref="#cb7-9"aria-hidden="true"tabindex="-1"></a><spanclass="co">Diagonale zu ueberschreiben zerstoert die positive Semidefinitheit - die</span></span>
<spanid="cb7-10"><ahref="#cb7-10"aria-hidden="true"tabindex="-1"></a><spanclass="co">Matrix kann dadurch einen negativen Eigenwert bekommen und unmoegliche</span></span>
<spanid="cb7-11"><ahref="#cb7-11"aria-hidden="true"tabindex="-1"></a><spanclass="co">Korrelationen (> 1) implizieren. Dieses Programm baut Sigma stattdessen als</span></span>
<spanid="cb7-12"><ahref="#cb7-12"aria-hidden="true"tabindex="-1"></a><spanclass="co">D * C * D aus Volatilitaeten und einer echten Korrelationsmatrix -</span></span>
<spanid="cb7-13"><ahref="#cb7-13"aria-hidden="true"tabindex="-1"></a><spanclass="co">konstruktionsbedingt immer PSD.</span></span>
<spanid="cb7-15"><ahref="#cb7-15"aria-hidden="true"tabindex="-1"></a><spanclass="co">Zusaetzlich: Multistart, weil SLSQP nur lokale Optima findet.</span></span>
<spanid="cb7-32"><ahref="#cb7-32"aria-hidden="true"tabindex="-1"></a>ALPHA <spanclass="op">=</span><spanclass="fl">1.0</span><spanclass="co"># Gewicht des Ertrags</span></span>
<spanid="cb7-33"><ahref="#cb7-33"aria-hidden="true"tabindex="-1"></a>BETA <spanclass="op">=</span><spanclass="fl">0.015</span><spanclass="co"># Gewicht der Entropie-Diversifikation</span></span>
<spanid="cb7-39"><ahref="#cb7-39"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Sigma = D * C * D. Immer PSD, wenn C eine gueltige Korrelationsmatrix ist."""</span></span>
<spanid="cb7-70"><ahref="#cb7-70"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""alpha und beta werden durchgereicht - so bleibt die Funktion seiteneffektfrei."""</span></span>
<spanid="cb7-87"><ahref="#cb7-87"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Pruefung der Kovarianzmatrix:"</span>)</span>
<spanid="cb7-93"><ahref="#cb7-93"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- Multistart: SLSQP findet nur lokale Optima -----------------------</span></span>
<spanid="cb7-97"><ahref="#cb7-97"aria-hidden="true"tabindex="-1"></a> z <spanclass="op">=</span> rng.random(n) <spanclass="op">+</span><spanclass="fl">0.05</span></span>
<spanid="cb7-105"><ahref="#cb7-105"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss">Multistart mit </span><spanclass="sc">{</span><spanclass="bu">len</span>(startpunkte)<spanclass="sc">}</span><spanclass="ss"> Startpunkten:"</span>)</span>
<spanid="cb7-110"><ahref="#cb7-110"aria-hidden="true"tabindex="-1"></a><spanclass="cf">else</span><spanclass="st">"ACHTUNG: verschiedene lokale Optima gefunden!"</span>))</span>
<spanid="cb7-122"><ahref="#cb7-122"aria-hidden="true"tabindex="-1"></a><spanclass="ss">f"(Maximum bei Gleichgewichtung: </span><spanclass="sc">{</span>np<spanclass="sc">.</span>log(n)<spanclass="sc">:.4f}</span><spanclass="ss">)"</span>)</span>
<spanid="cb7-131"><ahref="#cb7-131"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- Vergleich: was passiert ohne Entropieterm? ----------------------</span></span>
<spanid="cb7-132"><ahref="#cb7-132"aria-hidden="true"tabindex="-1"></a> ohne <spanclass="op">=</span> optimiere(np.ones(n) <spanclass="op">/</span> n, beta<spanclass="op">=</span><spanclass="fl">0.0</span>)</span>
<spanid="cb7-133"><ahref="#cb7-133"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st">--- Wirkung des Entropieterms ---"</span>)</span>
<spanid="cb7-134"><ahref="#cb7-134"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" Mit Entropie (beta=</span><spanclass="sc">{</span>BETA<spanclass="sc">}</span><spanclass="ss">): Gewichte </span><spanclass="sc">{</span>np<spanclass="sc">.</span><spanclass="bu">round</span>(w_opt <spanclass="op">*</span><spanclass="dv">100</span>, <spanclass="dv">1</span>)<spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb7-135"><ahref="#cb7-135"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" Ohne Entropie (beta=0): Gewichte </span><spanclass="sc">{</span>np<spanclass="sc">.</span><spanclass="bu">round</span>(ohne.x <spanclass="op">*</span><spanclass="dv">100</span>, <spanclass="dv">1</span>)<spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb7-136"><ahref="#cb7-136"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Der Entropieterm zieht Kapital aus der Spitzenposition heraus,"</span>)</span>
<spanid="cb7-137"><ahref="#cb7-137"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" ohne dass eine harte Obergrenze noetig waere."</span>)</span>
<p><strong>Vergleichen Sie diese Ausgabe mit der eingangs beschriebenen fehlerhaften Konstruktion.</strong> Die Eigenwerte sind jetzt <strong>alle positiv</strong> (kleinster: 0,0047 statt −0,084), die größte Korrelation liegt bei 0,55 statt bei unmöglichen 6,8 — und der Multistart bestätigt mit einer Spannweite von <spanclass="math inline">4{,}3\times10^{-13}</span>, dass alle zehn Startpunkte im selben Optimum landen. Das ist der empirische Beleg für Konvexität — mit einer indefiniten Matrix wäre das nicht möglich gewesen.</p>
<p>Interessant ist auch die Wirkung des Entropieterms: Er verschiebt nur rund 7 Prozentpunkte von Krypto weg — aber er hebt die Kleinstpositionen von 0,1 % auf 2,8 % bzw. 0,5 %. Eine harte Obergrenze hätte stattdessen genau bei der Schranke abgeschnitten und alles andere unverändert gelassen. <strong>Der weiche Term verteilt, die harte Schranke kappt.</strong></p>
<blockquote>
<p><strong>💻 Code-Durchgang: die drei Lehren</strong></p>
<oltype="1">
<li><strong><code>baue_kovarianz()</code> statt Diagonal-Überschreiben.</strong> Der Weg <spanclass="math inline">\boldsymbol{\Sigma} = \mathbf{D}\mathbf{C}\mathbf{D}</span> (Volatilitäten mal Korrelationsmatrix) ist der einzige, der die Zulässigkeit garantiert — und er ist zugleich <strong>interpretierbar</strong>: Man gibt Volatilitäten und Korrelationen vor, also genau die Größen, über die man fachlich diskutiert.</li>
<li><strong>Multistart.</strong> Zehn Startpunkte, und die Spannweite der Ergebnisse wird gemessen. Sind alle gleich, ist das ein starkes Indiz für Konvexität. Weichen sie ab, wissen Sie sofort, dass Sie einem lokalen Optimum aufsitzen. Diese fünf Zeilen sollten in jedem SLSQP-Projekt stehen.</li>
<li><strong>Analytischer Gradient.</strong><code>jac=gradient</code> spart nicht nur Zeit — die numerische Approximation von Ableitungen ist bei Termen wie <spanclass="math inline">\ln w</span> nahe null numerisch heikel.</li>
<p><strong>Zur Skalierung:</strong> Mit <spanclass="math inline">\alpha = 1</span> ist der Ertragsterm (<spanclass="math inline">\approx 0{,}1</span>) zehnmal größer als der Varianzterm (<spanclass="math inline">\approx 0{,}01</span>). Das Modell ist also stark renditegetrieben. Die Aufgabe <em>Effekt der Gewichtung untersuchen</em> (<ahref="#sec:qp-nlp-uebungsaufgaben">Abschnitt 11.7</a>) lässt Sie diese Balance untersuchen — sie ist eine fachliche Entscheidung, keine technische.</p>
<h2id="sec:qp-nlp-nichtkonvex">11.6 Jenseits der Konvexität: lokale Optima, Multistart und MINLP</h2>
<p>Alles bisher in diesem Kapitel stand unter einer Bedingung: <strong>Konvexität</strong>. Sie ist der Grund, warum CVXPY eine Optimalitätsgarantie geben kann und warum die KKT-Bedingungen nicht nur notwendig, sondern auch hinreichend sind.</p>
<p>Dieser Abschnitt handelt davon, was passiert, wenn diese Bedingung fehlt — und das ist in der Praxis oft genug der Fall.</p>
<h3id="woher-nicht-konvexität-im-alltag-kommt">Woher Nicht-Konvexität im Alltag kommt</h3>
<p>Nicht aus exotischer Mathematik, sondern aus ganz gewöhnlichen betriebswirtschaftlichen Effekten:</p>
<table>
<colgroup>
<colstyle="width: 33%"/>
<colstyle="width: 33%"/>
<colstyle="width: 33%"/>
</colgroup>
<thead>
<trclass="header">
<th>Ursache</th>
<th>Beispiel</th>
<th>Warum nicht konvex</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td><strong>Mengenrabatte</strong></td>
<td>Stückpreis fällt mit der Bestellmenge</td>
<td>Kostenfunktion wird konkav — Sprünge nach unten belohnen große Lose</td>
</tr>
<trclass="even">
<td><strong>Skaleneffekte</strong></td>
<td>Stückkosten sinken mit der Losgröße</td>
<td>dasselbe Muster in der Produktion</td>
</tr>
<trclass="odd">
<td><strong>Produkte von Variablen</strong></td>
<td>„Menge mal Preis“, wenn beide entschieden werden</td>
<td><spanclass="math inline">x \cdot y</span> ist weder konvex noch konkav</td>
</tr>
<trclass="even">
<td><strong>Ja/Nein mal Menge</strong></td>
<td>Anlage läuft <em>und</em> wie stark</td>
<td>ganzzahlig + nichtlinear = MINLP</td>
</tr>
<trclass="odd">
<td><strong>Verhältnisse</strong></td>
<td>Auslastungsgrad, Rendite je eingesetztem Euro</td>
<td>Quotienten sind selten konvex</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>🎯 Merksatz</strong> Sobald ein <strong>Rabatt</strong>, ein <strong>Skaleneffekt</strong> oder ein <strong>Produkt zweier Entscheidungen</strong> im Modell steht, ist die Konvexität in Gefahr — und damit die Optimalitätsgarantie. Das ist kein Grund zur Panik, aber ein Grund, das Ergebnis anders zu behandeln.</p>
</blockquote>
<h3id="was-ein-lokales-optimum-praktisch-bedeutet">Was ein lokales Optimum praktisch bedeutet</h3>
<p><code>scipy.optimize.minimize</code> verweigert nichts. Es rechnet, meldet <code>success: True</code> und liefert eine Zahl. Diese Meldung heißt aber <strong>nicht</strong> „das ist das Optimum“, sondern nur:</p>
<blockquote>
<p><em>„Ich bin an einer Stelle angekommen, an der es in keine Richtung mehr bergab geht.“</em></p>
</blockquote>
<p>Bei einem konvexen Problem ist das dasselbe. Bei einem nicht-konvexen sind es zwei völlig verschiedene Aussagen. Das folgende Programm macht den Unterschied sichtbar.</p>
<spanid="cb9-5"><ahref="#cb9-5"aria-hidden="true"tabindex="-1"></a><spanclass="co">Kapitel QP/NLP: Was passiert, wenn die Konvexitaet fehlt.</span></span>
<spanid="cb9-7"><ahref="#cb9-7"aria-hidden="true"tabindex="-1"></a><spanclass="co">CVXPY verweigert nicht-konvexe Probleme - das ist sein Schutzmechanismus.</span></span>
<spanid="cb9-8"><ahref="#cb9-8"aria-hidden="true"tabindex="-1"></a><spanclass="co">scipy.optimize.minimize verweigert nichts. Es rechnet, meldet 'success: True'</span></span>
<spanid="cb9-9"><ahref="#cb9-9"aria-hidden="true"tabindex="-1"></a><spanclass="co">und liefert ein Ergebnis. Nur ist das Ergebnis dann kein Optimum, sondern</span></span>
<spanid="cb9-10"><ahref="#cb9-10"aria-hidden="true"tabindex="-1"></a><spanclass="co">irgendein lokales Minimum, das vom Startpunkt abhaengt.</span></span>
<spanid="cb9-12"><ahref="#cb9-12"aria-hidden="true"tabindex="-1"></a><spanclass="co">Beispiel aus dem Einkauf: 700 Tonnen Rohstoff werden auf vier Lieferanten</span></span>
<spanid="cb9-13"><ahref="#cb9-13"aria-hidden="true"tabindex="-1"></a><spanclass="co">verteilt. Jeder gewaehrt einen MENGENRABATT - der Stueckpreis faellt, je mehr</span></span>
<spanid="cb9-14"><ahref="#cb9-14"aria-hidden="true"tabindex="-1"></a><spanclass="co">man bei ihm bestellt:</span></span>
<spanid="cb9-18"><ahref="#cb9-18"aria-hidden="true"tabindex="-1"></a><spanclass="co">Genau das macht die Zielfunktion nicht-konvex: Grosse Bestellungen lohnen</span></span>
<spanid="cb9-19"><ahref="#cb9-19"aria-hidden="true"tabindex="-1"></a><spanclass="co">sich ueberproportional, es gibt also mehrere sinnvolle "Cluster"-Loesungen -</span></span>
<spanid="cb9-22"><ahref="#cb9-22"aria-hidden="true"tabindex="-1"></a><spanclass="co">Das Programm zeigt drei Dinge:</span></span>
<spanid="cb9-23"><ahref="#cb9-23"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 1. Ein einzelner Lauf liefert ein plausibles Ergebnis - ohne jede Warnung.</span></span>
<spanid="cb9-24"><ahref="#cb9-24"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 2. 200 Startpunkte foerdern mehrere verschiedene lokale Optima zutage.</span></span>
<spanid="cb9-25"><ahref="#cb9-25"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 3. Der Unterschied zwischen bestem und schlechtestem betraegt hier 8 %.</span></span>
<spanid="cb9-47"><ahref="#cb9-47"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Einkaufskosten bei mengenabhaengigem Stueckpreis.</span></span>
<spanid="cb9-49"><ahref="#cb9-49"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Der Rabatt waechst mit der Bestellmenge und laeuft gegen MAX_RABATT.</span></span>
<spanid="cb9-50"><ahref="#cb9-50"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Dadurch ist der Stueckpreis fallend - und die Gesamtkostenfunktion</span></span>
<spanid="cb9-51"><ahref="#cb9-51"aria-hidden="true"tabindex="-1"></a><spanclass="co"> nicht mehr konvex.</span></span>
<spanid="cb9-62"><ahref="#cb9-62"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Ein Lauf von einem gegebenen Startpunkt aus."""</span></span>
<spanid="cb9-68"><ahref="#cb9-68"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Zufaellige Aufteilung, die den Bedarf bereits erfuellt."""</span></span>
<spanid="cb9-75"><ahref="#cb9-75"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> name, m <spanclass="kw">in</span><spanclass="bu">zip</span>(NAMEN, menge):</span>
<spanid="cb9-76"><ahref="#cb9-76"aria-hidden="true"tabindex="-1"></a> anteil <spanclass="op">=</span> m <spanclass="op">/</span> BEDARF <spanclass="op">*</span><spanclass="dv">100</span></span>
<spanid="cb9-77"><ahref="#cb9-77"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span>name<spanclass="sc">:<10}</span><spanclass="ss"></span><spanclass="sc">{</span>m<spanclass="sc">:7.1f}</span><spanclass="ss"> t (</span><spanclass="sc">{</span>anteil<spanclass="sc">:4.1f}</span><spanclass="ss"> %)"</span>)</span>
<spanid="cb9-85"><ahref="#cb9-85"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span>BEDARF<spanclass="sc">:.0f}</span><spanclass="ss"> t Rohstoff auf </span><spanclass="sc">{</span><spanclass="bu">len</span>(NAMEN)<spanclass="sc">}</span><spanclass="ss"> Lieferanten mit Mengenrabatt."</span>)</span>
<spanid="cb9-87"><ahref="#cb9-87"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- 1. Ein einziger Lauf, so wie man es zuerst schreibt --------------</span></span>
<spanid="cb9-88"><ahref="#cb9-88"aria-hidden="true"tabindex="-1"></a> erster <spanclass="op">=</span> optimiere_von(np.full(<spanclass="bu">len</span>(NAMEN), BEDARF <spanclass="op">/</span><spanclass="bu">len</span>(NAMEN)))</span>
<spanid="cb9-89"><ahref="#cb9-89"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss">[1] EIN Lauf, Startpunkt 'gleichmaessig verteilt'"</span>)</span>
<spanid="cb9-93"><ahref="#cb9-93"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st"> Nichts an dieser Ausgabe deutet darauf hin, dass etwas fehlt."</span>)</span>
<spanid="cb9-95"><ahref="#cb9-95"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- 1b. Der kaufmaennisch naheliegende Startpunkt --------------------</span></span>
<spanid="cb9-96"><ahref="#cb9-96"aria-hidden="true"tabindex="-1"></a><spanclass="co"># "Kaufe bei den beiden Lieferanten mit dem guenstigsten Basispreis" -</span></span>
<spanid="cb9-97"><ahref="#cb9-97"aria-hidden="true"tabindex="-1"></a><spanclass="co"># West SE (45) und Ost GmbH (47), jeweils bis zur Kapazitaetsgrenze.</span></span>
<spanid="cb9-105"><ahref="#cb9-105"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss">[1b] EIN Lauf, Startpunkt 'die zwei mit dem guenstigsten Basispreis'"</span>)</span>
<spanid="cb9-119"><ahref="#cb9-119"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Ergebnisse, die sich um weniger als 1 Cent unterscheiden, sind dasselbe</span></span>
<spanid="cb9-120"><ahref="#cb9-120"aria-hidden="true"tabindex="-1"></a><spanclass="co"># lokale Optimum - zusammenfassen, sonst zaehlt man Rundungsrauschen.</span></span>
<spanid="cb9-128"><ahref="#cb9-128"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" darunter </span><spanclass="sc">{</span><spanclass="bu">len</span>(optima)<spanclass="sc">}</span><spanclass="ss"> VERSCHIEDENE lokale Optima:"</span>)</span>
<spanid="cb9-130"><ahref="#cb9-130"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span><spanclass="st">'Rang'</span><spanclass="sc">:>5}</span><spanclass="ss"></span><spanclass="sc">{</span><spanclass="st">'Kosten'</span><spanclass="sc">:>13}</span><spanclass="ss"></span><spanclass="sc">{</span><spanclass="st">'Abstand zum besten'</span><spanclass="sc">:>20}</span><spanclass="ss"> Aufteilung (t)"</span>)</span>
<spanid="cb9-138"><ahref="#cb9-138"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- 3. Was das kostet ------------------------------------------------</span></span>
<spanid="cb9-141"><ahref="#cb9-141"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" WAS AUF DEM SPIEL STEHT"</span>)</span>
<spanid="cb9-152"><ahref="#cb9-152"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Bemerkenswert: Der kaufmaennisch NAHELIEGENDE Startpunkt fuehrt in"</span>)</span>
<spanid="cb9-153"><ahref="#cb9-153"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" das schlechteste Ergebnis von allen - schlechter als jedes der 200"</span>)</span>
<spanid="cb9-154"><ahref="#cb9-154"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" zufaellig gefundenen lokalen Optima. Wer beim guenstigsten"</span>)</span>
<spanid="cb9-155"><ahref="#cb9-155"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Basispreis anfaengt, uebersieht, dass hier der Mengenrabatt"</span>)</span>
<spanid="cb9-156"><ahref="#cb9-156"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" entscheidet und nicht der Listenpreis."</span>)</span>
<spanid="cb9-159"><ahref="#cb9-159"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Drei Konsequenzen fuer die Praxis:"</span>)</span>
<spanid="cb9-160"><ahref="#cb9-160"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" 1. 'success: True' heisst bei nicht-konvexen Problemen NICHT 'optimal'."</span>)</span>
<spanid="cb9-161"><ahref="#cb9-161"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Es heisst nur: 'Ich bin an einer Stelle angekommen, an der es in"</span>)</span>
<spanid="cb9-162"><ahref="#cb9-162"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" keine Richtung mehr bergab geht.'"</span>)</span>
<spanid="cb9-163"><ahref="#cb9-163"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" 2. Ein einzelner Lauf ist wertlos. Nehmen Sie viele Startpunkte und"</span>)</span>
<spanid="cb9-164"><ahref="#cb9-164"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" berichten Sie die STREUUNG mit - sie ist Ihre einzige Auskunft"</span>)</span>
<spanid="cb9-165"><ahref="#cb9-165"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" darueber, wie zerklueftet die Landschaft ist."</span>)</span>
<spanid="cb9-166"><ahref="#cb9-166"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" 3. Auch Multistart liefert KEINE Garantie. Dass hier nichts unter"</span>)</span>
<spanid="cb9-167"><ahref="#cb9-167"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span>bester<spanclass="sc">:,.2f}</span><spanclass="ss"> EUR gefunden wurde, beweist nicht, dass es nichts gibt."</span>)</span>
<td>Ein völlig normaler Staffelrabatt — und genau er zerstört die Konvexität. Man braucht keine exotische Mathematik dafür.</td>
</tr>
<trclass="even">
<td>Zusammenfassen mit <code>> 0.01</code></td>
<td>Rundungsrauschen von echten Optima trennen</td>
<td>Ohne diesen Schritt zählt man 200 „verschiedene“ Optima, die sich in der zehnten Nachkommastelle unterscheiden.</td>
</tr>
<trclass="odd">
<td>200 zufällige Startpunkte</td>
<td>Multistart</td>
<td>Die einzige praktikable Auskunft darüber, wie zerklüftet die Landschaft ist — und trotzdem <strong>kein Beweis</strong>.</td>
</tr>
<trclass="even">
<td>Lauf <code>[1b]</code></td>
<td>der kaufmännisch naheliegende Startpunkt</td>
<td>Die eigentliche Pointe: Er ist der <strong>schlechteste von allen</strong>.</td>
</tr>
</tbody>
</table>
</blockquote>
<h3id="die-zahlen-im-klartext">Die Zahlen im Klartext</h3>
<ul>
<li>Der gleichmäßige Startpunkt findet <strong>24 724 €</strong> — zufällig das beste gefundene Optimum.</li>
<li>Der kaufmännisch naheliegende Startpunkt („die zwei mit dem günstigsten Basispreis“) findet <strong>28 619 €</strong> — 15,8 % schlechter, und schlechter als <em>jedes</em> der 200 zufällig gefundenen lokalen Optima.</li>
<li>Beide Läufe melden <code>success: True</code>. Nichts an der Ausgabe unterscheidet sie.</li>
</ul>
<p>Der Grund für das schlechte Abschneiden des „vernünftigen“ Starts ist lehrreich: Wer beim günstigsten <strong>Listenpreis</strong> beginnt, folgt genau dem Kriterium, das hier nicht entscheidet. Ausschlaggebend ist der Rabatt — und Süd KG hat mit 35 % den höchsten, obwohl sein Basispreis der teuerste ist. Eine plausible Heuristik führt den Optimierer damit zielsicher ins falsche Tal.</p>
<blockquote>
<p><strong>⚠️ Typische Fehler bei nicht-konvexen Problemen</strong></p>
<ul>
<li><strong><code>success: True</code> als „optimal“ lesen.</strong> Es heißt nur „konvergiert“.</li>
<li><strong>Einen einzigen Lauf berichten.</strong> Ohne Streuung über mehrere Startpunkte ist die Zahl nicht einordbar.</li>
<li><strong>Den „vernünftigen“ Startpunkt für den besten halten.</strong> Er ist oft der schlechteste, weil er einer Heuristik folgt, die genau das ignoriert, was das Problem schwer macht.</li>
<li><strong>Multistart für einen Beweis halten.</strong> Dass nichts Besseres gefunden wurde, beweist nicht, dass es nichts Besseres gibt.</li>
</ul>
</blockquote>
<h3id="ausblick-minlp-und-globale-solver">Ausblick: MINLP und globale Solver</h3>
<p>Wird zusätzlich noch ganzzahlig entschieden — „welche Anlage läuft überhaupt“ <em>und</em> „wie stark“ —, entsteht ein <strong>MINLP</strong> (<em>Mixed-Integer Nonlinear Program</em>), die schwierigste der in diesem Buch behandelten Klassen. Dafür gibt es eigene Werkzeuge:</p>
<table>
<colgroup>
<colstyle="width: 33%"/>
<colstyle="width: 33%"/>
<colstyle="width: 33%"/>
</colgroup>
<thead>
<trclass="header">
<th>Werkzeug</th>
<th>Art</th>
<th>Was es leistet</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td><strong>Ipopt</strong> (über <code>cyipopt</code> oder Pyomo)</td>
<td>lokaler NLP-Solver</td>
<td>Innere-Punkte-Verfahren für große, glatte NLPs mit tausenden Variablen — deutlich leistungsfähiger als <code>scipy</code>, aber ebenfalls <strong>nur lokal</strong></td>
<td>Bindet alle oben genannten an, ohne dass das Modell umgeschrieben werden muss</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>⚠️ Zu diesem Abschnitt gehört kein lauffähiges Programm.</strong> Ipopt, Bonmin und Couenne sind C++-Pakete, die über die Python-Installation hinaus systemweit eingerichtet werden müssen (<code>conda install -c conda-forge ipopt cyipopt</code> oder eine Distributionspaketquelle). Anders als bei allen übrigen Programmen dieses Buches konnten wir den Code deshalb nicht auf jedem Zielsystem ausführen — und drucken hier bewusst <strong>keine</strong> Ausgabe ab, die wir nicht selbst erzeugt haben. Der folgende Ausschnitt zeigt die Anbindung; prüfen Sie das Ergebnis auf Ihrem System selbst nach.</p>
</blockquote>
<divclass="sourceCode"id="cb11"><preclass="sourceCode python"><codeclass="sourceCode python"><spanid="cb11-1"><ahref="#cb11-1"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Ipopt ueber Pyomo - Anbindungsmuster, NICHT ausgefuehrt.</span></span>
<spanid="cb11-11"><ahref="#cb11-11"aria-hidden="true"tabindex="-1"></a><spanclass="op">*</span> modell.q[i] <spanclass="cf">for</span> i <spanclass="kw">in</span><spanclass="bu">range</span>(<spanclass="dv">4</span>)),</span>
<spanid="cb11-19"><ahref="#cb11-19"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Ipopt ist nicht installiert - siehe Hinweis oben."</span>)</span></code></pre></div>
<p><strong>Die Strategie bleibt in jedem Fall dieselbe:</strong> Ein lokaler Solver wie Ipopt löst <em>schneller</em> und <em>größer</em> als <code>scipy</code> — er löst aber nicht <em>globaler</em>. Multistart bleibt nötig. Erst ein globaler Solver (SCIP, Couenne, BARON) ersetzt ihn, und den bezahlt man mit Rechenzeit, die um Größenordnungen höher liegt.</p>
<blockquote>
<p><strong>🎯 Merksatz</strong> Bei nicht-konvexen Problemen ist die ehrliche Berichterstattung wichtiger als die letzte Nachkommastelle: <em>„Bester gefundener Wert 24 724 € aus 200 Startpunkten; die Streuung reicht bis 26 690 €.“</em> Das ist eine belastbare Aussage. <em>„Das Optimum liegt bei 24 724,19 €“</em> ist es nicht.</p>
<p><strong>Aufgabe 11.1 ⭐ — Konvexität einordnen.</strong><spanclass="math inline">\mathbf{P}</span> hat die Eigenwerte <spanclass="math inline">(2{,}5;\ 0{,}0;\ 1{,}3)</span>. Ist das QP konvex? Streng konvex? Ist die Lösung eindeutig? Was bedeutet der Eigenwert 0 anschaulich?</p>
<p><strong>Aufgabe 11.2 ⭐ — Komplementärer Schlupf.</strong> Ein QP liefert <spanclass="math inline">w = (0{,}4;\ 0{,}0;\ 0{,}6)</span> mit Bedingung <spanclass="math inline">w \ge 0</span> und Multiplikatoren <spanclass="math inline">\lambda = (0{,}0;\ 0{,}03;\ 0{,}0)</span>. Ist das mit KKT verträglich? Was sagt <spanclass="math inline">\lambda_2 = 0{,}03</span> wirtschaftlich?</p>
<p><strong>Aufgabe 11.3 ⭐⭐ — KKT von Hand.</strong> Lösen Sie mit KKT vollständig von Hand: <spanclass="math display">\min\ x_1^2 + x_2^2 \qquad\text{u. d. N.}\qquad x_1 + x_2 \ge 4</span> Geben Sie <spanclass="math inline">x^*</span>, <spanclass="math inline">\lambda^*</span> und die Interpretation von <spanclass="math inline">\lambda^*</span> an. Prüfen Sie mit CVXPY.</p>
<p><strong>Aufgabe 11.4 ⭐⭐ — Ungültige Kovarianzmatrix erkennen.</strong> Prüfen Sie, ob folgende Korrelationsmatrix möglich ist, und begründen Sie: <spanclass="math display">\mathbf{C} = \begin{pmatrix}1{,}0&0{,}9&-0{,}9\\0{,}9&1{,}0&0{,}9\\-0{,}9&0{,}9&1{,}0\end{pmatrix}</span> (Tipp: Wenn A stark mit B korreliert und B stark mit C, kann A dann stark <strong>negativ</strong> mit C korrelieren?)</p>
<p><strong>Aufgabe 11.5 ⭐⭐⭐ — Effekt der Gewichtung untersuchen.</strong> Variieren Sie in <code>Entropie_Maximierte_Allokation.py</code> systematisch <spanclass="math inline">\alpha \in \{0{,}1;\ 0{,}5;\ 1;\ 5\}</span> und <spanclass="math inline">\beta \in \{0;\ 0{,}005;\ 0{,}015;\ 0{,}05\}</span>. Stellen Sie für jede Kombination Rendite, Volatilität und Entropie in einer Tabelle dar. (a) Wie verändert <spanclass="math inline">\beta</span> die Konzentration im Krypto-Titel? (b) Ab welchem <spanclass="math inline">\beta</span> nähert sich die Lösung der Gleichgewichtung? (c) Was ist Ihre Empfehlung — und mit welcher Begründung würden Sie sie einem Anlageausschuss vorlegen?</p>
<p><strong>Aufgabe 11.6 ⭐⭐⭐ — Nicht-Konvexität demonstrieren.</strong> Bauen Sie bewusst eine ungültige „Kovarianzmatrix“ (per <ahref="mailto:A@A.T"class="email">A@A.T</a> mit anschließend überschriebener Diagonale) und lösen Sie das Modell mit 20 Startpunkten. Dokumentieren Sie: Wie viele verschiedene Optima entstehen? Wie groß ist der Unterschied zwischen bestem und schlechtestem Ergebnis? Kann die „Portfoliovarianz“ negativ werden?</p>
<hr/>
<h2id="sec:qp-nlp-denkfehler">11.8 Finde den Denkfehler</h2>
<p>Ein Analyst soll das Risiko eines Portfolios aus drei Anlagen minimieren. Die Korrelationen hat er aus drei verschiedenen Quartalsberichten zusammengetragen — jede für sich plausibel:</p>
<ul>
<li>A und B laufen stark gleich: <spanclass="math inline">\rho_{AB} = 0{,}9</span></li>
<li>B und C laufen stark gleich: <spanclass="math inline">\rho_{BC} = 0{,}9</span></li>
<li>A und C laufen <strong>gegen</strong>einander: <spanclass="math inline">\rho_{AC} = -0{,}9</span></li>
</ul>
<p>Alle drei schwanken mit 20 %. Leerverkäufe sind in seinem Mandat erlaubt, die Gewichte dürfen also negativ werden; sie müssen sich nur zu 100 % summieren.</p>
<p>Sein erster Versuch mit CVXPY bricht ab:</p>
<pre><code>DCPError: Problem does not follow DCP rules.
The objective is not DCP. Its following subexpressions are not: QuadForm(...)</code></pre>
<p>Er hält das für eine Einschränkung der Bibliothek und weicht auf <code>scipy</code> aus:</p>
<p>Ein Portfolio mit einer <strong>Varianz von <spanclass="math inline">-0{,}254</span></strong>. Der Analyst notiert erfreut ein „risikofreies Portfolio mit negativer Schwankung“.</p>
<p><strong>Ihre Aufgabe:</strong> (a) Können A und B gleichlaufen, B und C gleichlaufen — und A und C zugleich gegenläufig sein? Prüfen Sie es, ohne zu rechnen, an einem anschaulichen Beispiel. (b) Berechnen Sie die Eigenwerte von <spanclass="math inline">\mathbf{R}</span> mit <code>np.linalg.eigvalsh(R)</code>. Was sagt das Vorzeichen? (c) Warum ist die negative Varianz kein Fehler von <code>scipy</code>, sondern die logische Folge der Eingabe — und warum meldet es trotzdem <code>success: True</code>? (d) Warum war die Fehlermeldung von CVXPY die <strong>hilfreichste</strong> Zeile des ganzen Vorgangs, und was tut man stattdessen?</p>
<p><strong>🎯 Merksatz</strong> Eine Kovarianzmatrix ist kein Behälter für einzeln geschätzte Zahlen. Sie ist ein <strong>geometrisches Objekt</strong>: Ihre Einträge hängen voneinander ab, und nicht jede Kombination von Korrelationen existiert überhaupt. Zusammengetragene Korrelationen aus verschiedenen Quellen sind fast nie widerspruchsfrei — das ist einer der häufigsten Fehler in der Risikomodellierung überhaupt.</p>
<p>Genau eine Antwort ist jeweils richtig. Auflösung in <ahref="anhang-loesungen.html#quiz-loesung-qp-nlp">Anhang A</a>.</p>
<p><strong>1. CVXPY lehnt Ihr Modell mit <code>DCPError: Problem does not follow DCP rules</code> ab. Was ist die richtige Reaktion?</strong> (a) Auf <code>scipy.optimize.minimize</code> ausweichen, das die Formulierung akzeptiert. (b) Die Meldung ernst nehmen: CVXPY sagt, dass es für diese Formulierung <strong>keine Optimalitätsgarantie</strong> geben kann. Entweder das Problem konvex umformulieren — oder bewusst auf ein lokales Verfahren mit Multistart wechseln. (c) Die Toleranzen lockern.</p>
<p><strong>2. <code>scipy.optimize.minimize</code> meldet für ein Modell mit Mengenrabatten <code>success: True</code>. Was wissen Sie damit über die gefundene Lösung?</strong> (a) Sie ist optimal. (b) Sie ist ein lokales Minimum — es geht von dort in keine Richtung mehr bergab. Ob es anderswo ein besseres gibt, ist damit offen. (c) Sie ist zulässig, aber möglicherweise nicht einmal ein lokales Minimum.</p>
<p><strong>3. Der Lagrange-Multiplikator einer bindenden Nebenbedingung beträgt <spanclass="math inline">\lambda^* = 4</span>. Was bedeutet das?</strong> (a) Die Nebenbedingung wird viermal verletzt. (b) Vier Einheiten der Ressource sind übrig. (c) Lockert man die Bedingung um eine kleine Einheit, verbessert sich der Zielwert um etwa 4 — es ist der Schattenpreis, genau wie im LP.</p>
<li><strong>Quadratische Programme</strong> sind die Brücke zur Portfoliotheorie: Risiko ist eine quadratische Form <spanclass="math inline">\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}</span>.</li>
<li><strong>KKT</strong> verallgemeinert Lagrange auf Ungleichungen. Die Multiplikatoren sind die Schattenpreise aus <ahref="lp.html#kap-lp">Kapitel 5</a> — nur allgemeiner.</li>
<li><strong>Komplementärer Schlupf</strong> ist der beste Selbsttest für jede Optimierung.</li>
<li><strong>Kovarianzmatrizen immer prüfen</strong> und aus Volatilitäten plus Korrelationsmatrix konstruieren, nie durch Manipulation einzelner Einträge.</li>
<li><strong>SLSQP liefert nur lokale Optima.</strong> Multistart ist Pflicht, nicht Kür — und <code>success: True</code> heißt „konvergiert“, nicht „optimal“. Im Kapitelbeispiel liegen fünf lokale Optima 8 % auseinander, und ausgerechnet der kaufmännisch naheliegende Startpunkt ist der schlechteste.</li>
<li><strong>Nicht-Konvexität kommt aus dem Alltag</strong>, nicht aus der Theorie: Mengenrabatte, Skaleneffekte, Produkte zweier Entscheidungen. Wo eines davon im Modell steht, ist die Optimalitätsgarantie weg.</li>
<li><strong>Berichten Sie bei nicht-konvexen Problemen die Streuung mit</strong>, nicht die letzte Nachkommastelle. „Bester Wert aus 200 Startpunkten, Spanne bis …“ ist belastbar, ein einzelner Wert ist es nicht.</li>
</ul>
<p><strong>Ausblick.</strong><ahref="unsicherheit.html#kap-unsicherheit">Kapitel 12</a> gibt die Annahme auf, dass die Parameter überhaupt bekannt sind. Wir lernen, mit Szenarien, Erwartungswerten und Worst Cases umzugehen.</p>