<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{idx: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{idx: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{idx: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"><summary>Teil III:
<p><strong>Wofür dieser Anhang gedacht ist:</strong> Etwas funktioniert nicht. Suchen Sie unten das Symptom, das Sie beobachten, und arbeiten Sie die Diagnoseschritte der Reihe nach ab.</p>
<p><strong>Bedeutung.</strong> Es gibt <strong>keinen einzigen Punkt</strong>, der alle Bedingungen gleichzeitig erfüllt. Das ist eine Aussage über Ihr Modell, nicht über den Solver.</p>
<h3id="diagnose-in-fünf-schritten">Diagnose in fünf Schritten</h3>
<p><strong>Schritt 1 — Trivialprüfungen von Hand.</strong> Rechnen Sie die offensichtlichen Bilanzen nach, <strong>bevor</strong> Sie den Solver befragen:</p>
<spanid="cb1-2"><ahref="#cb1-2"aria-hidden="true"tabindex="-1"></a><spanclass="cf">assert</span> anzahl_personal <spanclass="op">*</span> max_schichten <spanclass="op">>=</span> anzahl_schichten, <spanclass="st">"zu wenig Personal"</span></span>
<spanid="cb1-3"><ahref="#cb1-3"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> slot, fach <spanclass="kw">in</span><spanclass="bu">enumerate</span>(faecher):</span>
<spanid="cb1-4"><ahref="#cb1-4"aria-hidden="true"tabindex="-1"></a> qualifiziert <spanclass="op">=</span> [p <spanclass="cf">for</span> p <spanclass="kw">in</span> personal <spanclass="cf">if</span> fach <spanclass="kw">in</span> qualifikation[p]]</span>
<spanid="cb1-5"><ahref="#cb1-5"aria-hidden="true"tabindex="-1"></a><spanclass="cf">assert</span> qualifiziert, <spanclass="ss">f"Fuer Slot </span><spanclass="sc">{</span>slot<spanclass="sc">}</span><spanclass="ss"> (</span><spanclass="sc">{</span>fach<spanclass="sc">}</span><spanclass="ss">) gibt es niemanden"</span></span></code></pre></div>
<p>Erfahrungsgemäß findet dieser Schritt <strong>die Mehrzahl</strong> aller Fälle.</p>
<p><strong>Schritt 2 — Bedingungen einzeln abschalten.</strong> Kommentieren Sie Bedingungsgruppen nacheinander aus. Wird das Modell lösbar, ist die zuletzt entfernte Gruppe (mit-)verantwortlich.</p>
<p>Nach dem Lösen zeigen die Schlupfvariablen mit Wert > 0 <strong>präzise, welche Bedingung</strong> wo und um wie viel verletzt werden musste. Das ist zugleich die produktionstaugliche Lösung (Muster B18).</p>
<p><strong>Schritt 4 — Zeitfenster und Erreichbarkeit prüfen.</strong> Bei Scheduling und Routing: Ist jeder Termin überhaupt physisch erreichbar?</p>
<spanid="cb3-2"><ahref="#cb3-2"aria-hidden="true"tabindex="-1"></a><spanclass="cf">assert</span> distanz[depot][kunde] <spanclass="op"><=</span> spaet, <spanclass="ss">f"Kunde </span><spanclass="sc">{</span>kunde<spanclass="sc">}</span><spanclass="ss"> nicht rechtzeitig erreichbar"</span></span></code></pre></div>
<p><strong>Schritt 5 — Rundungs- und Einheitenfehler.</strong> Sind Kapazitäten in Stunden, Verbräuche aber in Minuten? Rechnen CP-SAT-Modelle mit ganzen Zahlen, wo Sie Nachkommastellen brauchen? Ein klassischer Fall: <code>sum(w) == 1</code> mit Gleitkommazahlen — nutzen Sie eine Toleranz oder rechnen Sie in Ganzzahlen (Promille).</p>
<h3id="wenn-die-fünf-schritte-nicht-reichen-den-konflikt-einkreisen">Wenn die fünf Schritte nicht reichen: den Konflikt einkreisen</h3>
<p><strong>Warum Schritt 2 so oft ins Leere läuft.</strong> Bedingungen einzeln abzuschalten funktioniert nur, solange es <em>einen</em> Schuldigen gibt. Überlagern sich zwei unabhängige Widersprüche, bleibt das Modell nach jeder einzelnen Abschaltung unlösbar — die Suche endet mit null Treffern, obwohl beide Widersprüche unverändert im Modell stehen. Genau dann steht man vor einem Modell mit tausenden Restriktionen und einem Statuscode.</p>
<p><strong>Der Deletion Filter</strong> dreht die Frage um. Er fragt nicht „ist <em>diese</em> Bedingung schuld?“, sondern „wird <em>diese</em> Bedingung für den Widerspruch überhaupt gebraucht?“ — und das ist eine Frage, die sich beantworten lässt:</p>
<blockquote>
<p>Nimm eine Bedingung versuchsweise heraus. Bleibt der Rest unlösbar, wurde sie nicht gebraucht: weg damit, endgültig. Wird der Rest lösbar, war sie beteiligt: sie bleibt.</p>
</blockquote>
<p>Nach genau einem Durchlauf über alle <spanclass="math inline">n</span> Bedingungen ist die übrig gebliebene Menge <strong>unreduzierbar</strong> — entfernt man aus ihr irgendeine Bedingung, ist der Rest lösbar. Das ist ein <em>Irreducible Infeasible Subset</em>, kurz IIS. Kommerzielle Solver bieten das als fertige Funktion an (<code>computeIIS</code> bei Gurobi, der Conflict Refiner bei CPLEX); im Open-Source-Werkzeugkasten dieses Buchs gibt es sie nicht — sie ist aber in zwanzig Zeilen selbst geschrieben, und sie kostet <spanclass="math inline">n</span> Aufrufe des Solvers statt <spanclass="math inline">2^n</span> durchprobierter Teilmengen.</p>
<spanid="cb4-5"><ahref="#cb4-5"aria-hidden="true"tabindex="-1"></a><spanclass="co">Anhang Fehlerdiagnose: Den Konflikt finden, der INFEASIBLE verursacht.</span></span>
<spanid="cb4-8"><ahref="#cb4-8"aria-hidden="true"tabindex="-1"></a><spanclass="co">einem unloesbaren Modell eine kleinste widerspruechliche Teilmenge von</span></span>
<spanid="cb4-9"><ahref="#cb4-9"aria-hidden="true"tabindex="-1"></a><spanclass="co">Bedingungen: Nimmt man aus ihr auch nur eine einzige Bedingung heraus, ist</span></span>
<spanid="cb4-10"><ahref="#cb4-10"aria-hidden="true"tabindex="-1"></a><spanclass="co">der Rest wieder loesbar.</span></span>
<spanid="cb4-12"><ahref="#cb4-12"aria-hidden="true"tabindex="-1"></a><spanclass="co">Das Programm zeigt sechs Dinge:</span></span>
<spanid="cb4-13"><ahref="#cb4-13"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 1. Der Solver meldet INFEASIBLE - und sonst nichts.</span></span>
<spanid="cb4-14"><ahref="#cb4-14"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 2. Die naive Suche ("jede Bedingung einmal weglassen") findet hier gar nichts.</span></span>
<spanid="cb4-15"><ahref="#cb4-15"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 3. Der Deletion Filter findet einen Konflikt in n Solveraufrufen.</span></span>
<spanid="cb4-16"><ahref="#cb4-16"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 4. Ein Konflikt ist selten einer: nach der Reparatur folgt der naechste.</span></span>
<spanid="cb4-17"><ahref="#cb4-17"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 5. "Den" kleinsten Konflikt gibt es nicht - dieses Modell enthaelt neun.</span></span>
<spanid="cb4-18"><ahref="#cb4-18"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 6. Welchen davon man zu sehen bekommt, steuert die Pruefreihenfolge.</span></span>
<spanid="cb4-20"><ahref="#cb4-20"aria-hidden="true"tabindex="-1"></a><spanclass="co">Abgrenzung zu Infeasibility_Diagnose.py (Kapitel Praxisfallen): Dort geht es</span></span>
<spanid="cb4-21"><ahref="#cb4-21"aria-hidden="true"tabindex="-1"></a><spanclass="co">um die Relaxation - das Modell soll trotz Widerspruch eine brauchbare Antwort</span></span>
<spanid="cb4-22"><ahref="#cb4-22"aria-hidden="true"tabindex="-1"></a><spanclass="co">liefern. Hier geht es um die Diagnose - welche Bedingungen widersprechen sich</span></span>
<spanid="cb4-23"><ahref="#cb4-23"aria-hidden="true"tabindex="-1"></a><spanclass="co">ueberhaupt. Beides zusammen ergibt den Umgang mit INFEASIBLE in Produktion.</span></span>
<spanid="cb4-35"><ahref="#cb4-35"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Jede Bedingung traegt einen sprechenden Namen - das ist keine Kosmetik,</span></span>
<spanid="cb4-36"><ahref="#cb4-36"aria-hidden="true"tabindex="-1"></a><spanclass="co"># sondern die Voraussetzung dafuer, dass der Befund lesbar wird.</span></span>
<spanid="cb4-37"><ahref="#cb4-37"aria-hidden="true"tabindex="-1"></a><spanclass="co"># (Name, Koeffizienten je Produkt, Richtung, rechte Seite)</span></span>
<spanid="cb4-55"><ahref="#cb4-55"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Welche Bedingungen koennte ein Planer im Ernstfall wirklich veraendern?</span></span>
<spanid="cb4-56"><ahref="#cb4-56"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Kapazitaeten lassen sich durch Sonderschichten dehnen, Liefervertraege und</span></span>
<spanid="cb4-61"><ahref="#cb4-61"aria-hidden="true"tabindex="-1"></a>aufrufe <spanclass="op">=</span><spanclass="dv">0</span><spanclass="co"># zaehlt jeden Solveraufruf mit - der Preis des Verfahrens</span></span>
<spanid="cb4-65"><ahref="#cb4-65"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Gibt es einen Punkt, der ALLE Bedingungen aus 'auswahl' erfuellt?</span></span>
<spanid="cb4-67"><ahref="#cb4-67"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Es wird nur Zulaessigkeit geprueft, keine Zielfunktion optimiert: die</span></span>
<spanid="cb4-68"><ahref="#cb4-68"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Zielfunktion ist konstant null. INFEASIBLE haengt nie an der Zielfunktion.</span></span>
<spanid="cb4-76"><ahref="#cb4-76"aria-hidden="true"tabindex="-1"></a><spanclass="co"># linprog kennt nur "<=", also ">=" durch Negation umdrehen</span></span>
<spanid="cb4-77"><ahref="#cb4-77"aria-hidden="true"tabindex="-1"></a> matrix.append(zeile <spanclass="cf">if</span> richtung <spanclass="op">==</span><spanclass="st">"<="</span><spanclass="cf">else</span><spanclass="op">-</span>zeile)</span>
<spanid="cb4-78"><ahref="#cb4-78"aria-hidden="true"tabindex="-1"></a> rechte_seite.append(grenze <spanclass="cf">if</span> richtung <spanclass="op">==</span><spanclass="st">"<="</span><spanclass="cf">else</span><spanclass="op">-</span>grenze)</span>
<spanid="cb4-86"><ahref="#cb4-86"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Verkleinert eine unloesbare Menge zu einer kleinsten unloesbaren Menge.</span></span>
<spanid="cb4-88"><ahref="#cb4-88"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Der Kern des Verfahrens ist eine einzige Regel: Nimm eine Bedingung</span></span>
<spanid="cb4-89"><ahref="#cb4-89"aria-hidden="true"tabindex="-1"></a><spanclass="co"> versuchsweise heraus. Bleibt der Rest unloesbar, wurde sie fuer den</span></span>
<spanid="cb4-90"><ahref="#cb4-90"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Widerspruch nicht gebraucht - sie darf endgueltig weg. Wird der Rest</span></span>
<spanid="cb4-91"><ahref="#cb4-91"aria-hidden="true"tabindex="-1"></a><spanclass="co"> loesbar, war sie beteiligt und muss bleiben.</span></span>
<spanid="cb4-93"><ahref="#cb4-93"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Nach genau einem Durchlauf ueber alle Bedingungen ist das Ergebnis</span></span>
<spanid="cb4-94"><ahref="#cb4-94"aria-hidden="true"tabindex="-1"></a><spanclass="co"> unreduzierbar: n Solveraufrufe statt 2^n Teilmengen.</span></span>
<spanid="cb4-97"><ahref="#cb4-97"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> i <spanclass="kw">in</span><spanclass="bu">list</span>(auswahl):</span>
<spanid="cb4-110"><ahref="#cb4-110"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"1. Was der Solver sagt"</span>)</span>
<spanid="cb4-113"><ahref="#cb4-113"aria-hidden="true"tabindex="-1"></a> status <spanclass="op">=</span><spanclass="st">"OPTIMAL"</span><spanclass="cf">if</span> ist_loesbar(alle) <spanclass="cf">else</span><spanclass="st">"INFEASIBLE"</span></span>
<spanid="cb4-115"><ahref="#cb4-115"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st">Mehr ist es nicht. Der Solver nennt keine Ursache, weil es die eine"</span>)</span>
<spanid="cb4-116"><ahref="#cb4-116"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Ursache nicht gibt: Unloesbarkeit ist eine Eigenschaft von Mengen von"</span>)</span>
<spanid="cb4-117"><ahref="#cb4-117"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Bedingungen, nicht von einzelnen Bedingungen."</span>)</span>
<spanid="cb4-122"><ahref="#cb4-122"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"2. Die naheliegende Idee - und warum sie hier scheitert"</span>)</span>
<spanid="cb4-124"><ahref="#cb4-124"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Jede Bedingung einmal weglassen und schauen, ob es dann geht:</span><spanclass="ch">\n</span><spanclass="st">"</span>)</span>
<spanid="cb4-125"><ahref="#cb4-125"aria-hidden="true"tabindex="-1"></a> treffer <spanclass="op">=</span> [i <spanclass="cf">for</span> i <spanclass="kw">in</span> alle <spanclass="cf">if</span> ist_loesbar([j <spanclass="cf">for</span> j <spanclass="kw">in</span> alle <spanclass="cf">if</span> j <spanclass="op">!=</span> i])]</span>
<spanid="cb4-126"><ahref="#cb4-126"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> i <spanclass="kw">in</span> alle:</span>
<spanid="cb4-127"><ahref="#cb4-127"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" ohne </span><spanclass="sc">{</span>BEDINGUNGEN[i][<spanclass="dv">0</span>]<spanclass="sc">:<26}</span><spanclass="ss"></span><spanclass="sc">{</span><spanclass="st">'loesbar'</span><spanclass="cf">if</span> i <spanclass="kw">in</span> treffer <spanclass="cf">else</span><spanclass="st">'weiter unloesbar'</span><spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb4-129"><ahref="#cb4-129"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Keine einzelne Bedingung ist schuld. Genau das ist der Normalfall -"</span>)</span>
<spanid="cb4-130"><ahref="#cb4-130"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"und der Grund, warum diese Suche in der Praxis so oft im Nichts endet."</span>)</span>
<spanid="cb4-136"><ahref="#cb4-136"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"3. Der Deletion Filter"</span>)</span>
<spanid="cb4-141"><ahref="#cb4-141"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span>kosten<spanclass="sc">}</span><spanclass="ss"> Solveraufrufe -> Konflikt aus </span><spanclass="sc">{</span><spanclass="bu">len</span>(konflikt)<spanclass="sc">}</span><spanclass="ss"> von "</span></span>
<spanid="cb4-143"><ahref="#cb4-143"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> name <spanclass="kw">in</span> namen(konflikt):</span>
<spanid="cb4-145"><ahref="#cb4-145"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st">Probe auf Unreduzierbarkeit - jede einzelne davon weglassen:"</span>)</span>
<spanid="cb4-146"><ahref="#cb4-146"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> i <spanclass="kw">in</span> konflikt:</span>
<spanid="cb4-148"><ahref="#cb4-148"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" ohne </span><spanclass="sc">{</span>BEDINGUNGEN[i][<spanclass="dv">0</span>]<spanclass="sc">:<26}</span><spanclass="ss"></span><spanclass="sc">{</span><spanclass="st">'loesbar'</span><spanclass="cf">if</span> rest_loesbar <spanclass="cf">else</span><spanclass="st">'UNLOESBAR - nicht minimal!'</span><spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb4-149"><ahref="#cb4-149"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss">Aufwand: </span><spanclass="sc">{</span>kosten<spanclass="sc">}</span><spanclass="ss"> Aufrufe. Alle Teilmengen durchzuprobieren waeren "</span></span>
<spanid="cb4-156"><ahref="#cb4-156"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"4. Ein Konflikt ist selten einer"</span>)</span>
<spanid="cb4-159"><ahref="#cb4-159"aria-hidden="true"tabindex="-1"></a> rest <spanclass="op">=</span> [i <spanclass="cf">for</span> i <spanclass="kw">in</span> alle <spanclass="cf">if</span> i <spanclass="op">!=</span> entfernt]</span>
<spanid="cb4-160"><ahref="#cb4-160"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Angenommen, '</span><spanclass="sc">{</span>BEDINGUNGEN[entfernt][<spanclass="dv">0</span>]<spanclass="sc">}</span><spanclass="ss">' laesst sich verhandeln"</span>)</span>
<spanid="cb4-161"><ahref="#cb4-161"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"und wird aus dem Modell genommen. Dann ist das Modell ..."</span>)</span>
<spanid="cb4-165"><ahref="#cb4-165"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"... immer noch unloesbar. Der Filter erneut:</span><spanclass="ch">\n</span><spanclass="st">"</span>)</span>
<spanid="cb4-167"><ahref="#cb4-167"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> name <spanclass="kw">in</span> namen(zweiter):</span>
<spanid="cb4-169"><ahref="#cb4-169"aria-hidden="true"tabindex="-1"></a> gemeinsam <spanclass="op">=</span><spanclass="bu">set</span>(zweiter) <spanclass="op">&</span><spanclass="bu">set</span>(konflikt)</span>
<spanid="cb4-170"><ahref="#cb4-170"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss">Ueberschneidung mit dem ersten Konflikt: </span><spanclass="sc">{</span><spanclass="bu">len</span>(gemeinsam)<spanclass="sc">}</span><spanclass="ss"> Bedingungen"</span>)</span>
<spanid="cb4-171"><ahref="#cb4-171"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Ein zweiter, unabhaengiger Widerspruch, den der erste verdeckt hat."</span>)</span>
<spanid="cb4-172"><ahref="#cb4-172"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Deshalb ist die Konfliktsuche eine Schleife, kein einzelner Aufruf:"</span>)</span>
<spanid="cb4-173"><ahref="#cb4-173"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"reparieren, neu suchen, bis das Modell loesbar ist."</span>)</span>
<spanid="cb4-178"><ahref="#cb4-178"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"5. Es gibt nicht DEN Konflikt"</span>)</span>
<spanid="cb4-180"><ahref="#cb4-180"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Derselbe Filter, nur eine andere Pruefreihenfolge - 200-mal gewuerfelt:</span><spanclass="ch">\n</span><spanclass="st">"</span>)</span>
<spanid="cb4-190"><ahref="#cb4-190"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="sc">{</span><spanclass="bu">len</span>(haeufigkeit)<spanclass="sc">}</span><spanclass="ss"> verschiedene minimale Konflikte in EINEM Modell."</span>)</span>
<spanid="cb4-191"><ahref="#cb4-191"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Der Filter liefert *einen* kleinsten Konflikt, nicht *den* kleinsten -"</span>)</span>
<spanid="cb4-192"><ahref="#cb4-192"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"den gibt es nicht. Alle oben sind gleichermassen korrekt."</span>)</span>
<spanid="cb4-197"><ahref="#cb4-197"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"6. Den Befund brauchbar machen"</span>)</span>
<spanid="cb4-199"><ahref="#cb4-199"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Der Filter wirft heraus, was er zuerst in die Hand bekommt. Wer die"</span>)</span>
<spanid="cb4-200"><ahref="#cb4-200"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"unveraenderlichen Bedingungen zuerst pruefen laesst, bekommt sie eher"</span>)</span>
<spanid="cb4-201"><ahref="#cb4-201"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"aus dem Befund heraus - und sieht dafuer mehr Stellschrauben.</span><spanclass="ch">\n</span><spanclass="st">"</span>)</span>
<spanid="cb4-202"><ahref="#cb4-202"aria-hidden="true"tabindex="-1"></a> fest <spanclass="op">=</span> [i <spanclass="cf">for</span> i <spanclass="kw">in</span> alle <spanclass="cf">if</span> BEDINGUNGEN[i][<spanclass="dv">0</span>] <spanclass="kw">not</span><spanclass="kw">in</span> VERHANDELBAR]</span>
<spanid="cb4-203"><ahref="#cb4-203"aria-hidden="true"tabindex="-1"></a> frei <spanclass="op">=</span> [i <spanclass="cf">for</span> i <spanclass="kw">in</span> alle <spanclass="cf">if</span> BEDINGUNGEN[i][<spanclass="dv">0</span>] <spanclass="kw">in</span> VERHANDELBAR]</span>
<spanid="cb4-206"><ahref="#cb4-206"aria-hidden="true"tabindex="-1"></a> konflikt <spanclass="op">=</span><spanclass="bu">sorted</span>(deletion_filter(ordnung))</span>
<spanid="cb4-207"><ahref="#cb4-207"aria-hidden="true"tabindex="-1"></a> stellschrauben <spanclass="op">=</span> [i <spanclass="cf">for</span> i <spanclass="kw">in</span> konflikt <spanclass="cf">if</span> BEDINGUNGEN[i][<spanclass="dv">0</span>] <spanclass="kw">in</span> VERHANDELBAR]</span>
<spanid="cb4-208"><ahref="#cb4-208"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span>titel<spanclass="sc">}</span><spanclass="ss"> -></span><spanclass="sc">{</span><spanclass="bu">len</span>(konflikt)<spanclass="sc">}</span><spanclass="ss"> Bedingungen, davon "</span></span>
<spanid="cb4-211"><ahref="#cb4-211"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st">Beide Befunde sind wahr. Nur einer davon nennt dem Planer etwas,"</span>)</span>
<spanid="cb4-212"><ahref="#cb4-212"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"das er tatsaechlich tun kann."</span>)</span>
<spanid="cb4-216"><ahref="#cb4-216"aria-hidden="true"tabindex="-1"></a> alle <spanclass="op">=</span><spanclass="bu">list</span>(<spanclass="bu">range</span>(<spanclass="bu">len</span>(BEDINGUNGEN)))</span>
<spanid="cb4-224"><ahref="#cb4-224"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Insgesamt </span><spanclass="sc">{</span>aufrufe<spanclass="sc">}</span><spanclass="ss"> Solveraufrufe fuer die gesamte Diagnose."</span>)</span>
<p><strong>Vier Dinge sind daran wichtig.</strong></p>
<oltype="1">
<li><strong>Die Zulässigkeitsprüfung braucht keine Zielfunktion.</strong><code>linprog</code> bekommt einen Nullvektor als Ziel. Unlösbarkeit hängt nie an der Zielfunktion — wer beim Diagnostizieren die echte Zielfunktion mitschleppt, bezahlt Rechenzeit für nichts.</li>
<li><strong>Gefiltert werden nur die aufgeführten Bedingungen, nicht die Variablenschranken.</strong> Die Nichtnegativität steht in <code>bounds</code> und bleibt in jedem Teilmodell stehen. Sitzt Ihr Widerspruch in den Schranken (<code>x >= 5</code> als Bound statt als Zeile), findet ihn der Filter nicht — schreiben Sie solche Grenzen dann als benannte Bedingung.</li>
<li><strong>Sprechende Namen sind kein Luxus.</strong> Der Befund ist genau so brauchbar wie die Namen, die darin vorkommen. <code>constraint_47, constraint_112, constraint_9</code> ist kein Befund.</li>
<li><strong>Der Filter liefert <em>einen</em> kleinsten Konflikt, nicht <em>den</em> kleinsten.</strong> Dieses Modell mit vierzehn Bedingungen enthält neun verschiedene minimale Konflikte; welchen man zu sehen bekommt, entscheidet allein die Prüfreihenfolge. Das ist kein Mangel des Verfahrens, sondern eine Eigenschaft des Problems.</li>
</ol>
<p><strong>Aus Punkt 4 folgt der eigentliche Praxisgriff.</strong> Weil der Filter bevorzugt das hinauswirft, was er zuerst in die Hand bekommt, überlegen Sie vor dem Lauf, welche Bedingungen Sie im Ernstfall tatsächlich ändern könnten — und lassen Sie die <em>unveränderlichen</em> zuerst prüfen. Dann bleiben eher die Stellschrauben im Befund stehen. Im Beispiel oben liefert die Reihenfolge „Verträge und Marktgrenzen zuerst“ einen Befund mit zwei veränderlichen Kapazitäten, die umgekehrte Reihenfolge einen mit nur einer. Beide Befunde sind wahr; nur der erste nennt dem Planer etwas, das er tun kann.</p>
<blockquote>
<p><strong>⚠️ Was der Filter kostet</strong> Ein Konflikt kostet <spanclass="math inline">n</span> Solverläufe. Bei 14 Bedingungen sind das 14 — bei 50 000 Restriktionen mit je zehn Sekunden Lösungszeit wären es knapp sechs Tage. Für große Modelle filtert man deshalb nicht einzeln, sondern <strong>gruppenweise</strong>: erst über Bedingungs<em>blöcke</em> (alle Kapazitäten, alle Verträge, alle Zeitfenster) laufen, dann den Filter nur noch innerhalb des einen Blocks anwenden, der übrig bleibt. Das ist derselbe Algorithmus auf einer gröberen Ebene und senkt die Zahl der Läufe um Größenordnungen.</p>
</blockquote>
<p><strong>Das Verhältnis zur Relaxation.</strong> Der Deletion Filter beantwortet die Frage <em>„was widerspricht sich?“</em>; die Schlupfvariablen aus Schritt 3 beantworten die Frage <em>„was tun wir jetzt?“</em>. Beide gehören in ein Produktionssystem, aber an verschiedene Stellen: der Filter in die Entwicklung und in die Fehlersuche, die Relaxation in den Betrieb (<code>Infeasibility_Diagnose.py</code>, <ahref="praxisfallen.html#sec:praxisfallen-die-fuenf-typischen-praxisfallen">Abschnitt 22.3</a>). Ein System, das im Ernstfall den Konflikt <em>benennt</em> und trotzdem einen Notfallplan <em>liefert</em>, hat beides.</p>
<blockquote>
<p><strong>🎯 Die eigentliche Lehre</strong><code>INFEASIBLE</code> in Produktion ist ein <strong>Entwurfsfehler</strong>, kein Betriebsfehler. Ein System, das im Ernstfall nur „geht nicht“ sagt, ist wertlos. Bauen Sie Schlupfvariablen von vornherein ein (<ahref="praxisfallen.html#sec:praxisfallen-die-fuenf-typischen-praxisfallen">Abschnitt 22.3</a>).</p>
</blockquote>
<hr/>
<h2id="c2-unbounded">C2 — UNBOUNDED</h2>
<p><strong>Bedeutung.</strong> Die Zielfunktion lässt sich unbegrenzt verbessern.</p>
<p><strong>Praktisch immer eine dieser drei Ursachen:</strong></p>
<oltype="1">
<li><strong>Eine Kapazitätsbedingung fehlt.</strong> Die häufigste Ursache. Prüfen Sie: Gibt es für jede Variable eine Obergrenze — entweder explizit oder implizit über eine Ressource?</li>
<li><strong>Vorzeichenfehler.</strong> Sie minimieren, wo Sie maximieren wollten (oder umgekehrt), und der Zielwert läuft in die falsche Richtung davon.</li>
<li><strong>Freie Variablen ohne Nichtnegativität.</strong><code>bounds=[(None, None)]</code> statt <code>[(0, None)]</code>.</li>
</ol>
<divclass="sourceCode"id="cb6"><preclass="sourceCode python"><codeclass="sourceCode python"><spanid="cb6-1"><ahref="#cb6-1"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Diagnose: kuenstliche Schranke einziehen und sehen, wohin es laeuft</span></span>
<spanid="cb6-5"><ahref="#cb6-5"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Diese Variablen laufen an die Kunstschranke: </span><spanclass="sc">{</span>gross<spanclass="sc">}</span><spanclass="ss">"</span>)</span></code></pre></div>
<p>Die so gefundenen Variablen sind die, denen eine echte Beschränkung fehlt.</p>
<p>Bei CVXPY und bei Schleifen über Szenarien ist oft der <strong>Aufbau</strong> der Engpass, nicht der Solver (siehe <ahref="cvar.html#sec:cvar-implementierung-cvar-portfolio-mit-reibung">Abschnitt 20.6</a>).</p>
<p><strong>2. Big-M zu groß?</strong> Der häufigste Grund für explodierende MILP-Laufzeiten. Setzen Sie <spanclass="math inline">M</span> auf die kleinste gültige Schranke (<ahref="milp.html#sec:milp-modellierungstricks-big-m-und-logische-bedingungen">Abschnitt 6.5</a>).</p>
<p><strong>3. Symmetrie im Modell?</strong> Identische Maschinen, austauschbare Mitarbeitende, gleichwertige Fahrzeuge — der Solver durchsucht alle Vertauschungen. Abhilfe: Ordnungsbedingungen (Muster B20).</p>
<p><strong>4. Falsche Solverfamilie?</strong> Zuordnungsprobleme mit MILP, Routing mit CP-SAT von Hand nachgebaut, konvexe Probleme mit <code>scipy.optimize.minimize</code> — jeweils Größenordnungen langsamer als das passende Werkzeug (<ahref="oekosystem.html#sec:oekosystem-wann-lohnt-sich-welche-ebene">Abschnitt 3.6</a>).</p>
<p><strong>Der Solver hat immer recht — bezogen auf das Modell, das Sie ihm gegeben haben.</strong> Wenn das Ergebnis unsinnig ist, beschreibt Ihr Modell nicht das Problem, das Sie meinen.</p>
<h3id="checkliste">Checkliste</h3>
<ulclass="task-list">
<li><label><inputtype="checkbox"/><strong>Einheiten.</strong> Euro gegen Cent? Stunden gegen Minuten? Jahres- gegen Tagesgrößen? (Ein Beispiel dafür finden Sie in <ahref="cvar.html#sec:cvar-implementierung-cvar-portfolio-mit-reibung">Abschnitt 20.6</a>.)</label></li>
<li><label><inputtype="checkbox"/><strong>Vorzeichen.</strong> Minimieren Sie, wo Sie maximieren wollten? Ist der Ertrag negativ eingetragen?</label></li>
<li><label><inputtype="checkbox"/><strong>Fehlende Bedingung.</strong> Formulieren Sie in Worten, was am Ergebnis falsch ist — meistens ist genau das die vergessene Nebenbedingung.</label></li>
<li><label><inputtype="checkbox"/><strong>Variablenbedeutung.</strong> Ist <spanclass="math inline">x_{ij}</span> „Person <spanclass="math inline">i</span> macht Aufgabe <spanclass="math inline">j</span>“ oder umgekehrt? Bei asymmetrischen Matrizen fällt das nicht auf.</label></li>
<li><label><inputtype="checkbox"/><strong>Index-Reihenfolge.</strong> Zeilen und Spalten vertauscht? Siehe C11.</label></li>
<divclass="sourceCode"id="cb10"><preclass="sourceCode python"><codeclass="sourceCode python"><spanid="cb10-1"><ahref="#cb10-1"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Nach JEDEM Lösen: alle Regeln explizit nachprüfen</span></span>
<spanid="cb10-2"><ahref="#cb10-2"aria-hidden="true"tabindex="-1"></a><spanclass="cf">assert</span><spanclass="bu">abs</span>(w.<spanclass="bu">sum</span>() <spanclass="op">-</span><spanclass="dv">1</span>) <spanclass="op"><</span><spanclass="fl">1e-6</span>, <spanclass="st">"Budget nicht eingehalten"</span></span>
<p>Diese Zeilen kosten Sekunden und fangen die Fehlerklasse ab, die der Solver <strong>nicht</strong> melden kann: dass Ihr Modell etwas anderes beschreibt, als Sie glauben.</p>
<spanid="cb11-2"><ahref="#cb11-2"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Kondition </span><spanclass="sc">{</span>eig<spanclass="sc">.</span><spanclass="bu">max</span>()<spanclass="op">/</span>eig<spanclass="sc">.</span><spanclass="bu">min</span>()<spanclass="sc">:.0f}</span><spanclass="ss"> | kleinster EW </span><spanclass="sc">{</span>eig<spanclass="sc">.</span><spanclass="bu">min</span>()<spanclass="sc">:.2e}</span><spanclass="ss">"</span>)</span>
<spanid="cb11-3"><ahref="#cb11-3"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Faustregel: Kondition > 1000 ist bedenklich, > 10000 kritisch</span></span></code></pre></div>
<h3id="derselbe-fehler-außerhalb-der-finanzwelt">Derselbe Fehler außerhalb der Finanzwelt</h3>
<p>Instabilität ist kein Portfolioproblem, sondern ein Skalierungsproblem — und es trifft jedes Modell, in dem Größen sehr verschiedener Ordnung nebeneinanderstehen. Euro-Beträge (<spanclass="math inline">10^7</span>) und Tonnen-Angaben (<spanclass="math inline">10^{-3}</span>) in derselben Matrix erzeugen eine Konditionszahl, die aus einer Datenunsicherheit von 0,1 % eine Lösungsänderung von 100 % machen kann. Die Konditionszahl ist genau die Obergrenze dieses Verstärkungsfaktors.</p>
<divclass="sourceCode"id="cb12"><preclass="sourceCode python"><codeclass="sourceCode python"><spanid="cb12-1"><ahref="#cb12-1"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Kondition der Restriktionsmatrix: </span><spanclass="sc">{</span>np<spanclass="sc">.</span>linalg<spanclass="sc">.</span>cond(A)<spanclass="sc">:.1e}</span><spanclass="ss">"</span>)</span>
<spanid="cb12-2"><ahref="#cb12-2"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Grobe Peilung: Wertebereiche der Koeffizienten anschauen</span></span>
<p>Klaffen die Beträge um mehr als drei, vier Größenordnungen auseinander, rechnen Sie in anderen Einheiten (Tausend Euro statt Euro, Kilogramm statt Tonnen) oder skalieren Sie Zeilen und Spalten automatisch. <code>Skalierung_Kondition.py</code> zeigt beides samt Ruiz-Equilibrierung (<ahref="fundament.html#sec:fundament-kondition">Abschnitt 2.7</a>).</p>
<p><strong>Symptom.</strong> Schattenpreise sind negativ, wo sie positiv sein sollten — oder null bei einer offensichtlich knappen Ressource.</p>
<h3id="die-drei-prüfungen">Die drei Prüfungen</h3>
<p><strong>1. Vorzeichenkonvention.</strong> Haben Sie zur Maximierung negiert?</p>
<divclass="sourceCode"id="cb13"><preclass="sourceCode python"><codeclass="sourceCode python"><spanid="cb13-1"><ahref="#cb13-1"aria-hidden="true"tabindex="-1"></a>schattenpreise <spanclass="op">=</span><spanclass="op">-</span>res.ineqlin.marginals <spanclass="co"># bei linprog nach Negation</span></span></code></pre></div>
<p>Genau das ist die Vorzeichenfalle aus <ahref="lp.html#sec:lp-die-vorzeichenfalle-bei-schattenpreisen">Abschnitt 5.7</a>.</p>
<p>Dieser Test ist <strong>unabhängig von jeder Vorzeichenkonvention</strong>. Nutzen Sie ihn.</p>
<p><strong>4. Wenn alle drei Prüfungen bestehen und der Wert trotzdem seltsam ist: Entartung.</strong> Die drei Prüfungen oben setzen voraus, dass es <em>einen</em> richtigen Schattenpreis gibt. Bei einem entarteten Optimum — mehr aktive Nebenbedingungen als Variablen — gibt es den nicht. Der Wert ist dann nicht falsch, sondern <strong>mehrdeutig</strong>: zwei korrekte Solver liefern für dasselbe Optimum verschiedene Dualwerte, und die numerische Gegenprobe aus Schritt 3 schlägt fehl, weil der Zuwachs nach links und nach rechts unterschiedlich ausfällt.</p>
<divclass="sourceCode"id="cb16"><preclass="sourceCode python"><codeclass="sourceCode python"><spanid="cb16-1"><ahref="#cb16-1"aria-hidden="true"tabindex="-1"></a>aktiv <spanclass="op">=</span><spanclass="bu">int</span>(np.<spanclass="bu">sum</span>(res.slack <spanclass="op"><</span><spanclass="fl">1e-9</span>)) <spanclass="co"># wie viele Bedingungen sind straff?</span></span>
<spanid="cb16-2"><ahref="#cb16-2"aria-hidden="true"tabindex="-1"></a><spanclass="cf">if</span> aktiv <spanclass="op">></span><spanclass="bu">len</span>(res.x):</span>
<spanid="cb16-3"><ahref="#cb16-3"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Entartet: </span><spanclass="sc">{</span>aktiv<spanclass="sc">}</span><spanclass="ss"> aktive Bedingungen bei </span><spanclass="sc">{</span><spanclass="bu">len</span>(res.x)<spanclass="sc">}</span><spanclass="ss"> Variablen."</span>)</span>
<spanid="cb16-4"><ahref="#cb16-4"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Einen einzelnen Schattenpreis zu berichten waere hier irrefuehrend."</span>)</span></code></pre></div>
<p>Die belastbare Antwort ist dann keine Zahl, sondern eine <strong>Spanne</strong> über alle optimalen Dualwerte — <code>Toleranzen_und_Entartung.py</code> rechnet sie vor (<ahref="lp.html#sec:lp-entartung">Abschnitt 5.9</a>).</p>
<p><strong>Symptom.</strong><code>linprog</code> sagt 530, CVXPY sagt 529,8.</p>
<p><strong>Diagnose:</strong> 1. <strong>Ist es nur Toleranz?</strong> Unterschiede unter <spanclass="math inline">10^{-6}</span> relativ sind normal. Vergleichen Sie <strong>nie</strong> mit <code>==</code>, sondern mit <code>np.isclose()</code>. 2. <strong>Ist das Modell wirklich identisch?</strong> Häufigster Fall: In einer Formulierung fehlt eine Bedingung oder das Vorzeichen einer Ungleichung ist gedreht. 3. <strong>Alternativoptima.</strong> Verschiedene Lösungsvektoren bei <strong>gleichem</strong> Zielwert sind kein Widerspruch — das Problem hat mehrere Optima (semidefinite Matrix, parallele Zielfunktion). 4. <strong>Ist eines der Ergebnisse gar nicht optimal?</strong> Status prüfen: <code>optimal_inaccurate</code> bedeutet, der Solver hat aufgegeben.</p>
<hr/>
<h2id="c8-dcperror">C8 — DCPError</h2>
<pre><code>cvxpy.error.DCPError: Problem does not follow DCP rules.</code></pre>
<p><strong>Bedeutung.</strong> CVXPY kann die Konvexität Ihres Ausdrucks nicht beweisen — es lehnt ab, statt ein Ergebnis ohne Garantie zu liefern. <strong>Das ist ein Feature.</strong></p>
<table>
<colgroup>
<colstyle="width: 44%"/>
<colstyle="width: 20%"/>
<colstyle="width: 35%"/>
</colgroup>
<thead>
<trclass="header">
<th>Problematischer Ausdruck</th>
<th>Warum</th>
<th>Lösung</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td><code>x * y</code> (beide Variablen)</td>
<td>bilinear</td>
<td>umformulieren oder MILP/NLP</td>
</tr>
<trclass="even">
<td><code>a / x</code></td>
<td>nicht konvex</td>
<td><code>cp.inv_pos(x)</code> bei <spanclass="math inline">x>0</span></td>
</tr>
<trclass="odd">
<td><code>cp.sqrt(cp.quad_form(w, S))</code></td>
<td><code>cp.sqrt</code> ist <strong>konkav</strong> und verlangt ein konkaves Argument — <code>quad_form</code> ist konvex. Der Ausdruck ist nie DCP, egal wie sauber <spanclass="math inline">S</span> ist</td>
<td>Cholesky <spanclass="math inline">S = LL^\top</span>, dann <code>cp.norm2(L.T @ w)</code>: die 2-Norm eines <strong>affinen</strong> Ausdrucks (siehe <code>CVaR_Portfolio.py</code>)</td>
</tr>
<trclass="even">
<td><code>cp.quad_form(w, S)</code> bei numerisch unsauberem <spanclass="math inline">S</span></td>
<td>Eigenwerte knapp unter null durch Rundung — CVXPY erkennt <spanclass="math inline">S</span> nicht als PSD</td>
<td><code>cp.psd_wrap(S)</code>, <strong>wenn</strong><spanclass="math inline">S</span> nachweislich PSD ist (z. B. eine Kovarianzmatrix). Behebt nur diese numerische Beanstandung, keine Regelverletzung im Aufbau</td>
<spanid="cb18-2"><ahref="#cb18-2"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> c <spanclass="kw">in</span> problem.constraints:</span>
<spanid="cb18-3"><ahref="#cb18-3"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(c.is_dcp(), c) <spanclass="co"># zeigt die Schuldige</span></span>
<p><strong>Ursache.</strong><code>ortools</code> und <code>highspy</code> bringen beide eine eigene HiGHS-Kopie mit; sie lassen sich auf vielen Systemen <strong>nicht im selben Prozess</strong> importieren (siehe <ahref="oekosystem.html#sec:oekosystem-ein-system-vier-programmieransaetze">Abschnitt 3.5</a>). Der Konflikt entsteht auch <strong>indirekt</strong>: <code>cvxpy</code> importiert ein installiertes <code>highspy</code> bei der Solver-Erkennung selbst mit — ein Skript, das erst <code>cvxpy</code> und dann <code>ortools</code> importiert, crasht daher mit derselben Meldung.</p>
<p><strong>Abhilfen (in dieser Reihenfolge):</strong> 1. Nur eines von beiden im selben Skript verwenden. 2. Getrennte Prozesse — ein <code>ProcessPoolExecutor</code> mit <code>mp_context="spawn"</code> und <code>max_tasks_per_child=1</code>, siehe <code>Ein_System_Vier_Ansaetze.py</code>. 3. Auf <code>highspy</code> verzichten: HiGHS ist ohnehin Backend von <code>scipy.optimize.linprog</code> und CVXPY. 4. Getrennte virtuelle Umgebungen.</p>
<p><strong>2. Zeitindizes prüfen.</strong><code>iloc[t]</code> gegen <code>iloc[t+1]</code> — die Gewichte von heute gehören auf die Rendite von morgen.</p>
<p><strong>3. Survivorship.</strong> Wurde das Universum nach Kriterien gefiltert, die erst am Ende bekannt waren (<code>dropna()</code> über den gesamten Zeitraum!)?</p>
<p><strong>4. Kosten.</strong> Sind Gebühren, Spread und Slippage verbucht?</p>
<p><strong>5. Wie viele Varianten haben Sie getestet?</strong> Bei 20 Versuchen findet man auch in reinem Rauschen eine „signifikante“ Strategie (<ahref="handelsmaschine.html#sec:handelsmaschine-die-fuenf-selbsttaeuschungen-des-backtestens">Abschnitt 21.6</a>).</p>
<p><strong>6. Nach Teilzeiträumen aufschlüsseln.</strong> Stammt die gesamte Überrendite aus einem einzigen Quartal?</p>
<p><strong>Symptom.</strong> Ergebnisse sind plausibel, aber falsch beschriftet — oder eine Sektorgrenze greift auf die falschen Titel.</p>
<p><strong>Ursache.</strong><code>yfinance</code> und viele andere Datenquellen liefern Spalten <strong>alphabetisch sortiert</strong>, nicht in der Reihenfolge Ihrer Anfrage.</p>
<divclass="sourceCode"id="cb21"><preclass="sourceCode python"><codeclass="sourceCode python"><spanid="cb21-1"><ahref="#cb21-1"aria-hidden="true"tabindex="-1"></a><spanclass="co"># IMMER nach dem Import:</span></span>
<spanid="cb21-2"><ahref="#cb21-2"aria-hidden="true"tabindex="-1"></a>kurse <spanclass="op">=</span> roh[<spanclass="st">"Close"</span>][tickers].dropna() <spanclass="co"># erzwingt die Reihenfolge</span></span>
<spanid="cb21-6"><ahref="#cb21-6"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Und Gruppen NIE ueber Positionsindizes definieren:</span></span>
<p>Wenn Sie nicht weiterkommen: <strong>Verkleinern Sie das Problem, bis Sie es von Hand nachrechnen können.</strong> Drei Mitarbeitende, zwei Schichten. Zwei Aktien, drei Tage. Fast jeder Modellierungsfehler wird an einer Instanz sichtbar, deren richtige Antwort Sie kennen — und fast keiner wird an einer Instanz sichtbar, deren Antwort Sie nicht unabhängig prüfen können.</p>