operations_research/OR_HTML_04/anhang-fehlerdiagnose.html
dschlueter e8abb66d75 Glossar-Links: Fachbegriffe im Fließtext automatisch verlinken
Auto-Linking im Build: Glossar-Begriffe werden im Fließtext automatisch
erkannt und als Link auf den Glossar-Eintrag gesetzt — erste Nennung je
Begriff je Datei, dezent gestrichelte Unterstreichung im HTML, hyperref
im PDF. Kein manuelles Markieren bei Textänderungen nötig.

Neue Infrastruktur:
- glossar_eintraege_04.py: glossar_slug() + LINK_TEXTE-Map (automatisch
  generiert aus EINTRAEGEN + Abkürzungs-Extraktion)
- erzeuge_glossar_04.py: jeder Eintrag bekommt {#gloss:<slug>}-Anchor
- build_version_04.py: resolve_glossar() — Auto-Linking + {gl:}-Marke
- web_04/site.css: .glossar-link (dezent, nicht wie Navigationslink)

5 neue Glossar-Einträge (waren als Begriff im Text, aber nicht im Glossar):
Solver, Scheduling, Graphen, Backtest, DCP (Disciplined Convex Programming)

Schutzmechanismen:
- Code-Blöcke, Inline-Code, {idx:}/{ref:}-Marken, Markdown-Links und
  Überschriften werden nicht verlinkt
- Verschachtelung verhindert: 'CP-SAT-Solver' als Ganzes, nicht 'CP'+[
  -'SAT'+[-'Solver'
- 'OR' als 2-Zeichen-Abkürzung nicht auto-verlinkt (False Positive in
  'OR-Tools'); {gl:OR} als manuelle Marke
- Glossar selbst nicht auto-verlinkt (sonst Self-Links)
- reflow_markdown(): ::: als Block-Grenze (sonst kollabiert fenced div)

619 Glossar-Links im Gesamtdokument, 239 Anchor im Glossar, 34 im Vorwort.
2026-09-10 17:40:29 +02:00

781 lines
110 KiB
HTML
Generated
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Anhang C: Fehlerdiagnose-Handbuch · Optimierte Entscheidungsfindung mit Python</title>
<script>
(function () {
try {
var t = localStorage.getItem("or-theme");
if (t) document.documentElement.setAttribute("data-theme", t);
} catch (e) {}
})();
</script>
<link rel="stylesheet" href="assets/highlight.css" />
<link rel="stylesheet" href="katex/katex.min.css" />
<script defer="" src="katex/katex.min.js"></script>
<script>document.addEventListener("DOMContentLoaded", function () {
var mathElements = document.getElementsByClassName("math");
var macros = [];
for (var i = 0; i < mathElements.length; i++) {
var texText = mathElements[i].firstChild;
if (mathElements[i].tagName == "SPAN") {
katex.render(texText.data, mathElements[i], {
displayMode: mathElements[i].classList.contains('display'),
throwOnError: false,
macros: macros,
fleqn: false
});
}}
// Der Browser springt zu einem #anker in der URL schon beim ersten Rendern
// an, BEVOR die KaTeX-Formeln oben im Text ihre finale Hoehe bekommen -
// durch den Reflow landet der Anker danach zu weit unten. Nach dem
// Formel-Rendering hier erneut zum Anker springen, das behebt es.
if (location.hash) {
var ziel = document.getElementById(decodeURIComponent(location.hash.slice(1)));
if (ziel) ziel.scrollIntoView({behavior: "instant", block: "start"});
}
});
</script>
<link rel="stylesheet" href="assets/site.css" />
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="icon-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/>
</symbol>
<symbol id="icon-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.2" y2="16.2"/>
</symbol>
<symbol id="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="12" cy="12" r="4.5"/>
<line x1="12" y1="1.5" x2="12" y2="4"/><line x1="12" y1="20" x2="12" y2="22.5"/>
<line x1="1.5" y1="12" x2="4" y2="12"/><line x1="20" y1="12" x2="22.5" y2="12"/>
<line x1="4.5" y1="4.5" x2="6.2" y2="6.2"/><line x1="17.8" y1="17.8" x2="19.5" y2="19.5"/>
<line x1="19.5" y1="4.5" x2="17.8" y2="6.2"/><line x1="6.2" y1="17.8" x2="4.5" y2="19.5"/>
</symbol>
<symbol id="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 14.5A8.5 8.5 0 1 1 9.5 4a6.8 6.8 0 0 0 10.5 10.5z"/>
</symbol>
<symbol id="icon-chevron-left" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="15 4 7 12 15 20"/>
</symbol>
<symbol id="icon-chevron-right" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="9 4 17 12 9 20"/>
</symbol>
<symbol id="icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="4 13 9.5 18.5 20 6"/>
</symbol>
<symbol id="icon-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 13.5V19a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 19V7A1.5 1.5 0 0 1 5 5.5h5.5"/>
<polyline points="14.5 3.5 20.5 3.5 20.5 9.5"/><line x1="11" y1="13" x2="20" y2="4"/>
</symbol>
<symbol id="icon-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 5.5A2 2 0 0 1 6 4h6v16H6a2 2 0 0 0-2 1.5z"/>
<path d="M20 5.5A2 2 0 0 0 18 4h-6v16h6a2 2 0 0 1 2 1.5z"/>
</symbol>
<symbol id="icon-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</symbol>
<symbol id="icon-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 3v12"/><polyline points="7 10 12 15 17 10"/><path d="M4 19.5h16"/>
</symbol></svg>
<header class="site-header">
<button type="button" class="icon-btn" id="sidebar-toggle" aria-label="Menü öffnen"><svg class="icon" aria-hidden="true"><use href="#icon-menu"></use></svg></button>
<a class="brand" href="index.html"><svg class="icon" aria-hidden="true"><use href="#icon-book"></use></svg> <span>Optimierte Entscheidungsfindung mit Python</span></a>
<div class="site-search">
<input id="suche-eingabe" type="search" placeholder="Suchen …" aria-label="Suche" autocomplete="off" />
<svg class="icon such-icon" aria-hidden="true"><use href="#icon-search"></use></svg>
<div id="suche-ergebnisse" class="suche-ergebnisse" hidden></div>
</div>
<button type="button" class="icon-btn" id="theme-toggle" aria-label="Darstellung umschalten">
<svg class="icon icon-sun" aria-hidden="true"><use href="#icon-sun"></use></svg><svg class="icon icon-moon" aria-hidden="true"><use href="#icon-moon"></use></svg>
</button>
</header>
<div class="site-body">
<div class="sidebar-overlay" id="sidebar-overlay" hidden></div>
<nav class="sidebar" id="sidebar" aria-label="Kapitelnavigation"><div class="sidebar-inhalt"><details class="sidebar-gruppe"><summary>Einstieg</summary><ul><li data-kapitel="vorwort.html"><a href="vorwort.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Vorwort & Lesehilfe</span></a></li><li data-kapitel="notation.html"><a href="notation.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Notation & Abkürzungen</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil I: Grundlagen des Operations Research</summary><ul><li data-kapitel="einfuehrung.html"><a href="einfuehrung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</span></a></li><li data-kapitel="fundament.html"><a href="fundament.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität{idx:Konvexität}</span></a></li><li data-kapitel="oekosystem.html"><a href="oekosystem.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a></li><li data-kapitel="modellierung.html"><a href="modellierung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 4: Vom Management-Wunsch zum Modell</span></a></li><li data-kapitel="synthese-grundlagen.html"><a href="synthese-grundlagen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil I</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil II: Die Kernverfahren der deterministischen Optimierung</summary><ul><li data-kapitel="lp.html"><a href="lp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise</span></a></li><li data-kapitel="milp.html"><a href="milp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound{idx:Branch-and-Bound}</span></a></li><li data-kapitel="cpsat.html"><a href="cpsat.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 7: Constraint Programming mit CP-SAT{idx:CP-SAT} — Logik, Scheduling und Zuweisung</span></a></li><li data-kapitel="graphen.html"><a href="graphen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP</span></a></li><li data-kapitel="metaheuristiken.html"><a href="metaheuristiken.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a></li><li data-kapitel="dekomposition.html"><a href="dekomposition.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></a></li><li data-kapitel="synthese-kernverfahren.html"><a href="synthese-kernverfahren.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil II</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil III: Nichtlinearität, Unsicherheit und mehrperiodige Dynamik</summary><ul><li data-kapitel="qp-nlp.html"><a href="qp-nlp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität</span></a></li><li data-kapitel="unsicherheit.html"><a href="unsicherheit.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit</span></a></li><li data-kapitel="dynamische-programmierung.html"><a href="dynamische-programmierung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung{idx:Bellman-Gleichung} und Order-Execution</span></a></li><li data-kapitel="mehrziel.html"><a href="mehrziel.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte</span></a></li><li data-kapitel="prognose.html"><a href="prognose.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 15: Predict-then-Optimize{idx:Predict-then-Optimize} — die bessere Prognose, die schlechtere Entscheidung</span></a></li><li data-kapitel="synthese-nichtlinear.html"><a href="synthese-nichtlinear.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil III</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil IV: Anwendungen — Energiewirtschaft und Finanzmärkte</summary><ul><li data-kapitel="bruecke.html"><a href="bruecke.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten</span></a></li><li data-kapitel="supplychain.html"><a href="supplychain.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit</span></a></li><li data-kapitel="finanzdaten.html"><a href="finanzdaten.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 18: Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage</span></a></li><li data-kapitel="markowitz.html"><a href="markowitz.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 19: Die moderne Portfoliotheorie nach Markowitz</span></a></li><li data-kapitel="cvar.html"><a href="cvar.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 20: Tail-Risiko, CVaR und Transaktionskosten{idx:Transaktionskosten}</span></a></li><li data-kapitel="handelsmaschine.html"><a href="handelsmaschine.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 21: Die vollständige quantitative Handelsmaschine</span></a></li><li data-kapitel="synthese-anwendungen.html"><a href="synthese-anwendungen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil IV</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil V: Praxis</summary><ul><li data-kapitel="praxisfallen.html"><a href="praxisfallen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 22: Praxisfallen und der Weg zum produktiven Einsatz</span></a></li><li data-kapitel="testing.html"><a href="testing.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 23: Testen, Messen, Ausliefern</span></a></li><li data-kapitel="synthese-praxis.html"><a href="synthese-praxis.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil V</span></a></li><li data-kapitel="projektwerkstatt.html"><a href="projektwerkstatt.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Projektwerkstatt</span></a></li></ul></details><details class="sidebar-gruppe" open><summary>Anhänge</summary><ul><li data-kapitel="anhang-loesungen.html"><a href="anhang-loesungen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang A: Lösungen zu allen Übungsaufgaben</span></a></li><li data-kapitel="anhang-modellierungsmuster.html"><a href="anhang-modellierungsmuster.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang B: Katalog der Modellierungsmuster</span></a></li><li data-kapitel="anhang-fehlerdiagnose.html" class="aktiv"><a href="anhang-fehlerdiagnose.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang C: Fehlerdiagnose-Handbuch</span></a></li><li data-kapitel="anhang-spickzettel.html"><a href="anhang-spickzettel.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang D: Spickzettel der Solver</span></a></li><li data-kapitel="anhang-glossar.html"><a href="anhang-glossar.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang E: Glossar</span></a></li><li data-kapitel="anhang-literatur.html"><a href="anhang-literatur.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang F: Literaturverzeichnis</span></a></li></ul></details><ul class="sidebar-extra"><li><a href="programme.html"><svg class="icon" aria-hidden="true"><use href="#icon-book"></use></svg> Beispielprogramme</a></li><li><a href="notebooks.html"><svg class="icon" aria-hidden="true"><use href="#icon-book"></use></svg> Notebooks</a></li><li><a href="Notebooks_04.zip" download><svg class="icon" aria-hidden="true"><use href="#icon-download"></use></svg> Download Notebooks als ZIP</a></li><li><a href="anhang-glossar.html"><svg class="icon" aria-hidden="true"><use href="#icon-book"></use></svg> Glossar</a></li><li><a href="stichwortverzeichnis.html"><svg class="icon" aria-hidden="true"><use href="#icon-search"></use></svg> Stichwortverzeichnis</a></li><li><a href="gesamtdokument.html"><svg class="icon" aria-hidden="true"><use href="#icon-external-link"></use></svg> Gesamtdokument (eine HTML-Seite)</a></li><li><a href="Operations_Research_mit_Python_Version_04.pdf" download><svg class="icon" aria-hidden="true"><use href="#icon-download"></use></svg> Download Buch als PDF</a></li></ul></div></nav>
<main class="content">
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="index.html">Start</a> <span>Anhang C: Fehlerdiagnose-Handbuch</span></nav>
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="anhang-modellierungsmuster.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Anhang B: Katalog der Modellierungsmuster</span></a><a class="prev-next-knopf prev-next-next" href="anhang-spickzettel.html"><span><small>Weiter</small>Anhang D: Spickzettel der Solver</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
<article>
<h1 id="anhang-fehlerdiagnose">Anhang C: Fehlerdiagnose-Handbuch</h1>
<blockquote>
<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>Programme:</strong><br />
<code>Konfliktsuche.py</code></p>
</blockquote>
<hr />
<h2 id="symptom-schnellübersicht">Symptom-Schnellübersicht</h2>
<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr class="header">
<th>Symptom</th>
<th>Abschnitt</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><a href="anhang-glossar.md#gloss:solver" class="glossar-link">Solver</a> meldet <code>INFEASIBLE</code></td>
<td><a href="#c1-infeasible">C1</a></td>
</tr>
<tr class="even">
<td><code>INFEASIBLE</code>, aber keine einzelne Bedingung ist schuld</td>
<td><a href="#c1-infeasible">C1</a>, <a href="anhang-glossar.md#gloss:deletion-filter" class="glossar-link">Deletion Filter</a></td>
</tr>
<tr class="odd">
<td>Solver meldet <code>UNBOUNDED</code></td>
<td><a href="#c2-unbounded">C2</a></td>
</tr>
<tr class="even">
<td>Solver läuft ewig / Timeout</td>
<td><a href="#c3-zu-langsam">C3</a></td>
</tr>
<tr class="odd">
<td>Ergebnis ist offensichtlich unsinnig</td>
<td><a href="#c4-unsinniges-ergebnis">C4</a></td>
</tr>
<tr class="even">
<td>Ergebnis ändert sich bei kleinsten Datenänderungen stark</td>
<td><a href="#c5-instabile-lösung">C5</a></td>
</tr>
<tr class="odd">
<td>Schattenpreise sehen falsch aus</td>
<td><a href="#c6-falsche-dualwerte">C6</a></td>
</tr>
<tr class="even">
<td>Zwei Solver liefern verschiedene Ergebnisse</td>
<td><a href="#c7-widersprüchliche-solver">C7</a></td>
</tr>
<tr class="odd">
<td><a href="anhang-glossar.md#gloss:cvxpy" class="glossar-link">CVXPY</a> wirft <code>DCPError</code></td>
<td><a href="#c8-dcperror">C8</a></td>
</tr>
<tr class="even">
<td><code>ImportError</code> bei ortools/highspy</td>
<td><a href="#c9-importfehler">C9</a></td>
</tr>
<tr class="odd">
<td><a href="anhang-glossar.md#gloss:backtest" class="glossar-link">Backtest</a> sieht zu gut aus</td>
<td><a href="#c10-verdächtig-guter-backtest">C10</a></td>
</tr>
<tr class="even">
<td>Ergebnisse sind falsch beschriftet</td>
<td><a href="#c11-vertauschte-spalten">C11</a></td>
</tr>
</tbody>
</table>
<hr />
<h2 id="c1-infeasible">C1 — INFEASIBLE</h2>
<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>
<h3 id="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>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="cf">assert</span> summe_angebot <span class="op">&gt;=</span> summe_bedarf, <span class="st">&quot;Angebot deckt Bedarf nicht&quot;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="cf">assert</span> anzahl_personal <span class="op">*</span> max_schichten <span class="op">&gt;=</span> anzahl_schichten, <span class="st">&quot;zu wenig Personal&quot;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> slot, fach <span class="kw">in</span> <span class="bu">enumerate</span>(faecher):</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> qualifiziert <span class="op">=</span> [p <span class="cf">for</span> p <span class="kw">in</span> personal <span class="cf">if</span> fach <span class="kw">in</span> qualifikation[p]]</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> qualifiziert, <span class="ss">f&quot;Fuer Slot </span><span class="sc">{</span>slot<span class="sc">}</span><span class="ss"> (</span><span class="sc">{</span>fach<span class="sc">}</span><span class="ss">) gibt es niemanden&quot;</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><strong>Schritt 3 — Schlupfvariablen einbauen (das wirksamste Mittel).</strong></p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co"># statt: modell.Add(bedingung)</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>schlupf <span class="op">=</span> modell.NewIntVar(<span class="dv">0</span>, obergrenze, <span class="ss">f&quot;schlupf_</span><span class="sc">{</span>name<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>modell.Add(linke_seite <span class="op">&lt;=</span> rechte_seite <span class="op">+</span> schlupf)</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>strafen.append(<span class="dv">10_000</span> <span class="op">*</span> schlupf)</span></code></pre></div>
<p>Nach dem Lösen zeigen die Schlupfvariablen mit Wert &gt; 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 <a href="anhang-glossar.md#gloss:scheduling" class="glossar-link">Scheduling</a> und Routing: Ist jeder Termin überhaupt physisch erreichbar?</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> kunde, (frueh, spaet) <span class="kw">in</span> <span class="bu">enumerate</span>(zeitfenster):</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> distanz[depot][kunde] <span class="op">&lt;=</span> spaet, <span class="ss">f&quot;Kunde </span><span class="sc">{</span>kunde<span class="sc">}</span><span class="ss"> nicht rechtzeitig erreichbar&quot;</span></span></code></pre></div>
<p><strong>Schritt 5 — Rundungs- und Einheitenfehler.</strong> Sind Kapazitäten in Stunden, Verbräuche aber in Minuten? Rechnen <a href="anhang-glossar.md#gloss:cp-sat" class="glossar-link">CP-SAT</a>-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>
<h3 id="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 <span class="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 <a href="anhang-glossar.md#gloss:iis-irreducible-infeasible-subset" class="glossar-link">IIS</a>. Kommerzielle Solver bieten das als fertige Funktion an (<code>computeIIS</code> bei <a href="anhang-glossar.md#gloss:gurobi" class="glossar-link">Gurobi</a>, 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 <span class="math inline">n</span> Aufrufe des Solvers statt <span class="math inline">2^n</span> durchprobierter Teilmengen.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Konfliktsuche.py</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="co">Anhang Fehlerdiagnose: Den Konflikt finden, der INFEASIBLE verursacht.</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="co">Ein Deletion Filter (auch: IIS, Irreducible Infeasible Subset) isoliert aus</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="co">einem unloesbaren Modell eine kleinste widerspruechliche Teilmenge von</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="co">Bedingungen: Nimmt man aus ihr auch nur eine einzige Bedingung heraus, ist</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="co">der Rest wieder loesbar.</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a><span class="co">Das Programm zeigt sechs Dinge:</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a><span class="co"> 1. Der Solver meldet INFEASIBLE - und sonst nichts.</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a><span class="co"> 2. Die naive Suche (&quot;jede Bedingung einmal weglassen&quot;) findet hier gar nichts.</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a><span class="co"> 3. Der Deletion Filter findet einen Konflikt in n Solveraufrufen.</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a><span class="co"> 4. Ein Konflikt ist selten einer: nach der Reparatur folgt der naechste.</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a><span class="co"> 5. &quot;Den&quot; kleinsten Konflikt gibt es nicht - dieses Modell enthaelt neun.</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a><span class="co"> 6. Welchen davon man zu sehen bekommt, steuert die Pruefreihenfolge.</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a><span class="co">Abgrenzung zu Infeasibility_Diagnose.py (Kapitel Praxisfallen): Dort geht es</span></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a><span class="co">um die Relaxation - das Modell soll trotz Widerspruch eine brauchbare Antwort</span></span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a><span class="co">liefern. Hier geht es um die Diagnose - welche Bedingungen widersprechen sich</span></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a><span class="co">ueberhaupt. Beides zusammen ergibt den Umgang mit INFEASIBLE in Produktion.</span></span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a><span class="co">Benoetigt: numpy, scipy</span></span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
<span id="cb4-29"><a href="#cb4-29" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-30"><a href="#cb4-30" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb4-31"><a href="#cb4-31" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> scipy.optimize <span class="im">import</span> linprog</span>
<span id="cb4-32"><a href="#cb4-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-33"><a href="#cb4-33" aria-hidden="true" tabindex="-1"></a>PRODUKTE <span class="op">=</span> [<span class="st">&quot;Rahmen&quot;</span>, <span class="st">&quot;Gehaeuse&quot;</span>, <span class="st">&quot;Deckel&quot;</span>, <span class="st">&quot;Traeger&quot;</span>, <span class="st">&quot;Halter&quot;</span>]</span>
<span id="cb4-34"><a href="#cb4-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-35"><a href="#cb4-35" aria-hidden="true" tabindex="-1"></a><span class="co"># Jede Bedingung traegt einen sprechenden Namen - das ist keine Kosmetik,</span></span>
<span id="cb4-36"><a href="#cb4-36" aria-hidden="true" tabindex="-1"></a><span class="co"># sondern die Voraussetzung dafuer, dass der Befund lesbar wird.</span></span>
<span id="cb4-37"><a href="#cb4-37" aria-hidden="true" tabindex="-1"></a><span class="co"># (Name, Koeffizienten je Produkt, Richtung, rechte Seite)</span></span>
<span id="cb4-38"><a href="#cb4-38" aria-hidden="true" tabindex="-1"></a>BEDINGUNGEN: <span class="bu">list</span>[<span class="bu">tuple</span>[<span class="bu">str</span>, <span class="bu">list</span>[<span class="bu">float</span>], <span class="bu">str</span>, <span class="bu">float</span>]] <span class="op">=</span> [</span>
<span id="cb4-39"><a href="#cb4-39" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Kapazitaet Montage&quot;</span>, [<span class="dv">3</span>, <span class="dv">2</span>, <span class="dv">1</span>, <span class="dv">4</span>, <span class="dv">2</span>], <span class="st">&quot;&lt;=&quot;</span>, <span class="dv">400</span>),</span>
<span id="cb4-40"><a href="#cb4-40" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Kapazitaet Lackieren&quot;</span>, [<span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>], <span class="st">&quot;&lt;=&quot;</span>, <span class="dv">150</span>),</span>
<span id="cb4-41"><a href="#cb4-41" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Kapazitaet Pruefung&quot;</span>, [<span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>], <span class="st">&quot;&lt;=&quot;</span>, <span class="dv">300</span>),</span>
<span id="cb4-42"><a href="#cb4-42" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Liefervertrag Rahmen&quot;</span>, [<span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>], <span class="st">&quot;&gt;=&quot;</span>, <span class="dv">40</span>),</span>
<span id="cb4-43"><a href="#cb4-43" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Liefervertrag Gehaeuse&quot;</span>, [<span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>], <span class="st">&quot;&gt;=&quot;</span>, <span class="dv">30</span>),</span>
<span id="cb4-44"><a href="#cb4-44" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Liefervertrag Deckel&quot;</span>, [<span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">0</span>], <span class="st">&quot;&gt;=&quot;</span>, <span class="dv">20</span>),</span>
<span id="cb4-45"><a href="#cb4-45" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Liefervertrag Traeger&quot;</span>, [<span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>], <span class="st">&quot;&gt;=&quot;</span>, <span class="dv">25</span>),</span>
<span id="cb4-46"><a href="#cb4-46" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Marktgrenze Rahmen&quot;</span>, [<span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>], <span class="st">&quot;&lt;=&quot;</span>, <span class="dv">120</span>),</span>
<span id="cb4-47"><a href="#cb4-47" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Marktgrenze Gehaeuse&quot;</span>, [<span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>], <span class="st">&quot;&lt;=&quot;</span>, <span class="dv">90</span>),</span>
<span id="cb4-48"><a href="#cb4-48" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Marktgrenze Deckel&quot;</span>, [<span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">0</span>], <span class="st">&quot;&lt;=&quot;</span>, <span class="dv">150</span>),</span>
<span id="cb4-49"><a href="#cb4-49" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Marktgrenze Halter&quot;</span>, [<span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">1</span>], <span class="st">&quot;&lt;=&quot;</span>, <span class="dv">200</span>),</span>
<span id="cb4-50"><a href="#cb4-50" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Mindestumsatz&quot;</span>, [<span class="dv">90</span>, <span class="dv">70</span>, <span class="dv">30</span>, <span class="dv">60</span>, <span class="dv">20</span>], <span class="st">&quot;&gt;=&quot;</span>, <span class="dv">12000</span>),</span>
<span id="cb4-51"><a href="#cb4-51" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Sortimentsbreite&quot;</span>, [<span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>], <span class="st">&quot;&gt;=&quot;</span>, <span class="dv">100</span>),</span>
<span id="cb4-52"><a href="#cb4-52" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Lackierbudget Schicht 2&quot;</span>, [<span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>], <span class="st">&quot;&lt;=&quot;</span>, <span class="dv">55</span>),</span>
<span id="cb4-53"><a href="#cb4-53" aria-hidden="true" tabindex="-1"></a>]</span>
<span id="cb4-54"><a href="#cb4-54" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-55"><a href="#cb4-55" aria-hidden="true" tabindex="-1"></a><span class="co"># Welche Bedingungen koennte ein Planer im Ernstfall wirklich veraendern?</span></span>
<span id="cb4-56"><a href="#cb4-56" aria-hidden="true" tabindex="-1"></a><span class="co"># Kapazitaeten lassen sich durch Sonderschichten dehnen, Liefervertraege und</span></span>
<span id="cb4-57"><a href="#cb4-57" aria-hidden="true" tabindex="-1"></a><span class="co"># Marktgrenzen nicht.</span></span>
<span id="cb4-58"><a href="#cb4-58" aria-hidden="true" tabindex="-1"></a>VERHANDELBAR <span class="op">=</span> {<span class="st">&quot;Kapazitaet Montage&quot;</span>, <span class="st">&quot;Kapazitaet Lackieren&quot;</span>,</span>
<span id="cb4-59"><a href="#cb4-59" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Kapazitaet Pruefung&quot;</span>, <span class="st">&quot;Lackierbudget Schicht 2&quot;</span>}</span>
<span id="cb4-60"><a href="#cb4-60" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-61"><a href="#cb4-61" aria-hidden="true" tabindex="-1"></a>aufrufe <span class="op">=</span> <span class="dv">0</span> <span class="co"># zaehlt jeden Solveraufruf mit - der Preis des Verfahrens</span></span>
<span id="cb4-62"><a href="#cb4-62" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-63"><a href="#cb4-63" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-64"><a href="#cb4-64" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> ist_loesbar(auswahl: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb4-65"><a href="#cb4-65" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Gibt es einen Punkt, der ALLE Bedingungen aus &#39;auswahl&#39; erfuellt?</span></span>
<span id="cb4-66"><a href="#cb4-66" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-67"><a href="#cb4-67" aria-hidden="true" tabindex="-1"></a><span class="co"> Es wird nur Zulaessigkeit geprueft, keine Zielfunktion optimiert: die</span></span>
<span id="cb4-68"><a href="#cb4-68" aria-hidden="true" tabindex="-1"></a><span class="co"> Zielfunktion ist konstant null. INFEASIBLE haengt nie an der Zielfunktion.</span></span>
<span id="cb4-69"><a href="#cb4-69" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-70"><a href="#cb4-70" aria-hidden="true" tabindex="-1"></a> <span class="kw">global</span> aufrufe</span>
<span id="cb4-71"><a href="#cb4-71" aria-hidden="true" tabindex="-1"></a> aufrufe <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb4-72"><a href="#cb4-72" aria-hidden="true" tabindex="-1"></a> matrix, rechte_seite <span class="op">=</span> [], []</span>
<span id="cb4-73"><a href="#cb4-73" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> auswahl:</span>
<span id="cb4-74"><a href="#cb4-74" aria-hidden="true" tabindex="-1"></a> _, koeffizienten, richtung, grenze <span class="op">=</span> BEDINGUNGEN[i]</span>
<span id="cb4-75"><a href="#cb4-75" aria-hidden="true" tabindex="-1"></a> zeile <span class="op">=</span> np.array(koeffizienten, dtype<span class="op">=</span><span class="bu">float</span>)</span>
<span id="cb4-76"><a href="#cb4-76" aria-hidden="true" tabindex="-1"></a> <span class="co"># linprog kennt nur &quot;&lt;=&quot;, also &quot;&gt;=&quot; durch Negation umdrehen</span></span>
<span id="cb4-77"><a href="#cb4-77" aria-hidden="true" tabindex="-1"></a> matrix.append(zeile <span class="cf">if</span> richtung <span class="op">==</span> <span class="st">&quot;&lt;=&quot;</span> <span class="cf">else</span> <span class="op">-</span>zeile)</span>
<span id="cb4-78"><a href="#cb4-78" aria-hidden="true" tabindex="-1"></a> rechte_seite.append(grenze <span class="cf">if</span> richtung <span class="op">==</span> <span class="st">&quot;&lt;=&quot;</span> <span class="cf">else</span> <span class="op">-</span>grenze)</span>
<span id="cb4-79"><a href="#cb4-79" aria-hidden="true" tabindex="-1"></a> ergebnis <span class="op">=</span> linprog(np.zeros(<span class="bu">len</span>(PRODUKTE)),</span>
<span id="cb4-80"><a href="#cb4-80" aria-hidden="true" tabindex="-1"></a> A_ub<span class="op">=</span>np.array(matrix), b_ub<span class="op">=</span>np.array(rechte_seite),</span>
<span id="cb4-81"><a href="#cb4-81" aria-hidden="true" tabindex="-1"></a> bounds<span class="op">=</span>(<span class="dv">0</span>, <span class="va">None</span>), method<span class="op">=</span><span class="st">&quot;highs&quot;</span>)</span>
<span id="cb4-82"><a href="#cb4-82" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> ergebnis.status <span class="op">==</span> <span class="dv">0</span></span>
<span id="cb4-83"><a href="#cb4-83" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-84"><a href="#cb4-84" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-85"><a href="#cb4-85" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> deletion_filter(auswahl: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">list</span>[<span class="bu">int</span>]:</span>
<span id="cb4-86"><a href="#cb4-86" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Verkleinert eine unloesbare Menge zu einer kleinsten unloesbaren Menge.</span></span>
<span id="cb4-87"><a href="#cb4-87" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-88"><a href="#cb4-88" aria-hidden="true" tabindex="-1"></a><span class="co"> Der Kern des Verfahrens ist eine einzige Regel: Nimm eine Bedingung</span></span>
<span id="cb4-89"><a href="#cb4-89" aria-hidden="true" tabindex="-1"></a><span class="co"> versuchsweise heraus. Bleibt der Rest unloesbar, wurde sie fuer den</span></span>
<span id="cb4-90"><a href="#cb4-90" aria-hidden="true" tabindex="-1"></a><span class="co"> Widerspruch nicht gebraucht - sie darf endgueltig weg. Wird der Rest</span></span>
<span id="cb4-91"><a href="#cb4-91" aria-hidden="true" tabindex="-1"></a><span class="co"> loesbar, war sie beteiligt und muss bleiben.</span></span>
<span id="cb4-92"><a href="#cb4-92" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-93"><a href="#cb4-93" aria-hidden="true" tabindex="-1"></a><span class="co"> Nach genau einem Durchlauf ueber alle Bedingungen ist das Ergebnis</span></span>
<span id="cb4-94"><a href="#cb4-94" aria-hidden="true" tabindex="-1"></a><span class="co"> unreduzierbar: n Solveraufrufe statt 2^n Teilmengen.</span></span>
<span id="cb4-95"><a href="#cb4-95" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-96"><a href="#cb4-96" aria-hidden="true" tabindex="-1"></a> rest <span class="op">=</span> <span class="bu">list</span>(auswahl)</span>
<span id="cb4-97"><a href="#cb4-97" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">list</span>(auswahl):</span>
<span id="cb4-98"><a href="#cb4-98" aria-hidden="true" tabindex="-1"></a> probe <span class="op">=</span> [j <span class="cf">for</span> j <span class="kw">in</span> rest <span class="cf">if</span> j <span class="op">!=</span> i]</span>
<span id="cb4-99"><a href="#cb4-99" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> ist_loesbar(probe):</span>
<span id="cb4-100"><a href="#cb4-100" aria-hidden="true" tabindex="-1"></a> rest <span class="op">=</span> probe</span>
<span id="cb4-101"><a href="#cb4-101" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> rest</span>
<span id="cb4-102"><a href="#cb4-102" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-103"><a href="#cb4-103" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-104"><a href="#cb4-104" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> namen(auswahl: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">list</span>[<span class="bu">str</span>]:</span>
<span id="cb4-105"><a href="#cb4-105" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> [BEDINGUNGEN[i][<span class="dv">0</span>] <span class="cf">for</span> i <span class="kw">in</span> auswahl]</span>
<span id="cb4-106"><a href="#cb4-106" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-107"><a href="#cb4-107" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-108"><a href="#cb4-108" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> teil_1_der_befund(alle: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb4-109"><a href="#cb4-109" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-110"><a href="#cb4-110" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;1. Was der Solver sagt&quot;</span>)</span>
<span id="cb4-111"><a href="#cb4-111" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-112"><a href="#cb4-112" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Modell: </span><span class="sc">{</span><span class="bu">len</span>(PRODUKTE)<span class="sc">}</span><span class="ss"> Variablen, </span><span class="sc">{</span><span class="bu">len</span>(BEDINGUNGEN)<span class="sc">}</span><span class="ss"> Bedingungen&quot;</span>)</span>
<span id="cb4-113"><a href="#cb4-113" aria-hidden="true" tabindex="-1"></a> status <span class="op">=</span> <span class="st">&quot;OPTIMAL&quot;</span> <span class="cf">if</span> ist_loesbar(alle) <span class="cf">else</span> <span class="st">&quot;INFEASIBLE&quot;</span></span>
<span id="cb4-114"><a href="#cb4-114" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Status: </span><span class="sc">{</span>status<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb4-115"><a href="#cb4-115" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">Mehr ist es nicht. Der Solver nennt keine Ursache, weil es die eine&quot;</span>)</span>
<span id="cb4-116"><a href="#cb4-116" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Ursache nicht gibt: Unloesbarkeit ist eine Eigenschaft von Mengen von&quot;</span>)</span>
<span id="cb4-117"><a href="#cb4-117" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Bedingungen, nicht von einzelnen Bedingungen.&quot;</span>)</span>
<span id="cb4-118"><a href="#cb4-118" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-119"><a href="#cb4-119" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-120"><a href="#cb4-120" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> teil_2_die_naive_suche(alle: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb4-121"><a href="#cb4-121" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-122"><a href="#cb4-122" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;2. Die naheliegende Idee - und warum sie hier scheitert&quot;</span>)</span>
<span id="cb4-123"><a href="#cb4-123" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-124"><a href="#cb4-124" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Jede Bedingung einmal weglassen und schauen, ob es dann geht:</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb4-125"><a href="#cb4-125" aria-hidden="true" tabindex="-1"></a> treffer <span class="op">=</span> [i <span class="cf">for</span> i <span class="kw">in</span> alle <span class="cf">if</span> ist_loesbar([j <span class="cf">for</span> j <span class="kw">in</span> alle <span class="cf">if</span> j <span class="op">!=</span> i])]</span>
<span id="cb4-126"><a href="#cb4-126" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> alle:</span>
<span id="cb4-127"><a href="#cb4-127" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; ohne </span><span class="sc">{</span>BEDINGUNGEN[i][<span class="dv">0</span>]<span class="sc">:&lt;26}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;loesbar&#39;</span> <span class="cf">if</span> i <span class="kw">in</span> treffer <span class="cf">else</span> <span class="st">&#39;weiter unloesbar&#39;</span><span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb4-128"><a href="#cb4-128" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss">Gefundene Schuldige: </span><span class="sc">{</span><span class="bu">len</span>(treffer)<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb4-129"><a href="#cb4-129" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Keine einzelne Bedingung ist schuld. Genau das ist der Normalfall -&quot;</span>)</span>
<span id="cb4-130"><a href="#cb4-130" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;und der Grund, warum diese Suche in der Praxis so oft im Nichts endet.&quot;</span>)</span>
<span id="cb4-131"><a href="#cb4-131" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-132"><a href="#cb4-132" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-133"><a href="#cb4-133" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> teil_3_der_filter(alle: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">list</span>[<span class="bu">int</span>]:</span>
<span id="cb4-134"><a href="#cb4-134" aria-hidden="true" tabindex="-1"></a> <span class="kw">global</span> aufrufe</span>
<span id="cb4-135"><a href="#cb4-135" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-136"><a href="#cb4-136" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;3. Der Deletion Filter&quot;</span>)</span>
<span id="cb4-137"><a href="#cb4-137" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-138"><a href="#cb4-138" aria-hidden="true" tabindex="-1"></a> vorher <span class="op">=</span> aufrufe</span>
<span id="cb4-139"><a href="#cb4-139" aria-hidden="true" tabindex="-1"></a> konflikt <span class="op">=</span> deletion_filter(alle)</span>
<span id="cb4-140"><a href="#cb4-140" aria-hidden="true" tabindex="-1"></a> kosten <span class="op">=</span> aufrufe <span class="op">-</span> vorher</span>
<span id="cb4-141"><a href="#cb4-141" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>kosten<span class="sc">}</span><span class="ss"> Solveraufrufe -&gt; Konflikt aus </span><span class="sc">{</span><span class="bu">len</span>(konflikt)<span class="sc">}</span><span class="ss"> von &quot;</span></span>
<span id="cb4-142"><a href="#cb4-142" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="bu">len</span>(BEDINGUNGEN)<span class="sc">}</span><span class="ss"> Bedingungen:</span><span class="ch">\n</span><span class="ss">&quot;</span>)</span>
<span id="cb4-143"><a href="#cb4-143" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name <span class="kw">in</span> namen(konflikt):</span>
<span id="cb4-144"><a href="#cb4-144" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; * </span><span class="sc">{</span>name<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb4-145"><a href="#cb4-145" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">Probe auf Unreduzierbarkeit - jede einzelne davon weglassen:&quot;</span>)</span>
<span id="cb4-146"><a href="#cb4-146" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> konflikt:</span>
<span id="cb4-147"><a href="#cb4-147" aria-hidden="true" tabindex="-1"></a> rest_loesbar <span class="op">=</span> ist_loesbar([j <span class="cf">for</span> j <span class="kw">in</span> konflikt <span class="cf">if</span> j <span class="op">!=</span> i])</span>
<span id="cb4-148"><a href="#cb4-148" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; ohne </span><span class="sc">{</span>BEDINGUNGEN[i][<span class="dv">0</span>]<span class="sc">:&lt;26}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;loesbar&#39;</span> <span class="cf">if</span> rest_loesbar <span class="cf">else</span> <span class="st">&#39;UNLOESBAR - nicht minimal!&#39;</span><span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb4-149"><a href="#cb4-149" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss">Aufwand: </span><span class="sc">{</span>kosten<span class="sc">}</span><span class="ss"> Aufrufe. Alle Teilmengen durchzuprobieren waeren &quot;</span></span>
<span id="cb4-150"><a href="#cb4-150" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;2^</span><span class="sc">{</span><span class="bu">len</span>(BEDINGUNGEN)<span class="sc">}</span><span class="ss"> = </span><span class="sc">{</span><span class="dv">2</span> <span class="op">**</span> <span class="bu">len</span>(BEDINGUNGEN)<span class="sc">:,}</span><span class="ss"> gewesen.&quot;</span>.replace(<span class="st">&quot;,&quot;</span>, <span class="st">&quot;.&quot;</span>))</span>
<span id="cb4-151"><a href="#cb4-151" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> konflikt</span>
<span id="cb4-152"><a href="#cb4-152" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-153"><a href="#cb4-153" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-154"><a href="#cb4-154" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> teil_4_der_naechste_konflikt(alle: <span class="bu">list</span>[<span class="bu">int</span>], konflikt: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb4-155"><a href="#cb4-155" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-156"><a href="#cb4-156" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;4. Ein Konflikt ist selten einer&quot;</span>)</span>
<span id="cb4-157"><a href="#cb4-157" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-158"><a href="#cb4-158" aria-hidden="true" tabindex="-1"></a> entfernt <span class="op">=</span> konflikt[<span class="dv">0</span>]</span>
<span id="cb4-159"><a href="#cb4-159" aria-hidden="true" tabindex="-1"></a> rest <span class="op">=</span> [i <span class="cf">for</span> i <span class="kw">in</span> alle <span class="cf">if</span> i <span class="op">!=</span> entfernt]</span>
<span id="cb4-160"><a href="#cb4-160" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Angenommen, &#39;</span><span class="sc">{</span>BEDINGUNGEN[entfernt][<span class="dv">0</span>]<span class="sc">}</span><span class="ss">&#39; laesst sich verhandeln&quot;</span>)</span>
<span id="cb4-161"><a href="#cb4-161" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;und wird aus dem Modell genommen. Dann ist das Modell ...&quot;</span>)</span>
<span id="cb4-162"><a href="#cb4-162" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> ist_loesbar(rest):</span>
<span id="cb4-163"><a href="#cb4-163" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;... loesbar. Fertig.&quot;</span>)</span>
<span id="cb4-164"><a href="#cb4-164" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span></span>
<span id="cb4-165"><a href="#cb4-165" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;... immer noch unloesbar. Der Filter erneut:</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb4-166"><a href="#cb4-166" aria-hidden="true" tabindex="-1"></a> zweiter <span class="op">=</span> deletion_filter(rest)</span>
<span id="cb4-167"><a href="#cb4-167" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name <span class="kw">in</span> namen(zweiter):</span>
<span id="cb4-168"><a href="#cb4-168" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; * </span><span class="sc">{</span>name<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb4-169"><a href="#cb4-169" aria-hidden="true" tabindex="-1"></a> gemeinsam <span class="op">=</span> <span class="bu">set</span>(zweiter) <span class="op">&amp;</span> <span class="bu">set</span>(konflikt)</span>
<span id="cb4-170"><a href="#cb4-170" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss">Ueberschneidung mit dem ersten Konflikt: </span><span class="sc">{</span><span class="bu">len</span>(gemeinsam)<span class="sc">}</span><span class="ss"> Bedingungen&quot;</span>)</span>
<span id="cb4-171"><a href="#cb4-171" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Ein zweiter, unabhaengiger Widerspruch, den der erste verdeckt hat.&quot;</span>)</span>
<span id="cb4-172"><a href="#cb4-172" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Deshalb ist die Konfliktsuche eine Schleife, kein einzelner Aufruf:&quot;</span>)</span>
<span id="cb4-173"><a href="#cb4-173" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;reparieren, neu suchen, bis das Modell loesbar ist.&quot;</span>)</span>
<span id="cb4-174"><a href="#cb4-174" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-175"><a href="#cb4-175" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-176"><a href="#cb4-176" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> teil_5_die_reihenfolge(alle: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb4-177"><a href="#cb4-177" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-178"><a href="#cb4-178" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;5. Es gibt nicht DEN Konflikt&quot;</span>)</span>
<span id="cb4-179"><a href="#cb4-179" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-180"><a href="#cb4-180" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Derselbe Filter, nur eine andere Pruefreihenfolge - 200-mal gewuerfelt:</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb4-181"><a href="#cb4-181" aria-hidden="true" tabindex="-1"></a> zufall <span class="op">=</span> np.random.default_rng(<span class="dv">0</span>)</span>
<span id="cb4-182"><a href="#cb4-182" aria-hidden="true" tabindex="-1"></a> haeufigkeit: <span class="bu">dict</span>[<span class="bu">tuple</span>[<span class="bu">int</span>, ...], <span class="bu">int</span>] <span class="op">=</span> {}</span>
<span id="cb4-183"><a href="#cb4-183" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> _ <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">200</span>):</span>
<span id="cb4-184"><a href="#cb4-184" aria-hidden="true" tabindex="-1"></a> ordnung <span class="op">=</span> [<span class="bu">int</span>(i) <span class="cf">for</span> i <span class="kw">in</span> zufall.permutation(alle)]</span>
<span id="cb4-185"><a href="#cb4-185" aria-hidden="true" tabindex="-1"></a> schluessel <span class="op">=</span> <span class="bu">tuple</span>(<span class="bu">sorted</span>(deletion_filter(ordnung)))</span>
<span id="cb4-186"><a href="#cb4-186" aria-hidden="true" tabindex="-1"></a> haeufigkeit[schluessel] <span class="op">=</span> haeufigkeit.get(schluessel, <span class="dv">0</span>) <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb4-187"><a href="#cb4-187" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> schluessel, anzahl <span class="kw">in</span> <span class="bu">sorted</span>(haeufigkeit.items(), key<span class="op">=</span><span class="kw">lambda</span> p: <span class="op">-</span>p[<span class="dv">1</span>]):</span>
<span id="cb4-188"><a href="#cb4-188" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span>anzahl<span class="sc">:3d}</span><span class="ss">x </span><span class="sc">{</span><span class="bu">len</span>(schluessel)<span class="sc">}</span><span class="ss"> Bedingungen: &quot;</span></span>
<span id="cb4-189"><a href="#cb4-189" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;, &#39;</span><span class="sc">.</span>join(namen(<span class="bu">list</span>(schluessel)))<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb4-190"><a href="#cb4-190" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="sc">{</span><span class="bu">len</span>(haeufigkeit)<span class="sc">}</span><span class="ss"> verschiedene minimale Konflikte in EINEM Modell.&quot;</span>)</span>
<span id="cb4-191"><a href="#cb4-191" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Der Filter liefert *einen* kleinsten Konflikt, nicht *den* kleinsten -&quot;</span>)</span>
<span id="cb4-192"><a href="#cb4-192" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;den gibt es nicht. Alle oben sind gleichermassen korrekt.&quot;</span>)</span>
<span id="cb4-193"><a href="#cb4-193" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-194"><a href="#cb4-194" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-195"><a href="#cb4-195" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> teil_6_den_befund_steuern(alle: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb4-196"><a href="#cb4-196" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-197"><a href="#cb4-197" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;6. Den Befund brauchbar machen&quot;</span>)</span>
<span id="cb4-198"><a href="#cb4-198" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-199"><a href="#cb4-199" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Der Filter wirft heraus, was er zuerst in die Hand bekommt. Wer die&quot;</span>)</span>
<span id="cb4-200"><a href="#cb4-200" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;unveraenderlichen Bedingungen zuerst pruefen laesst, bekommt sie eher&quot;</span>)</span>
<span id="cb4-201"><a href="#cb4-201" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;aus dem Befund heraus - und sieht dafuer mehr Stellschrauben.</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb4-202"><a href="#cb4-202" aria-hidden="true" tabindex="-1"></a> fest <span class="op">=</span> [i <span class="cf">for</span> i <span class="kw">in</span> alle <span class="cf">if</span> BEDINGUNGEN[i][<span class="dv">0</span>] <span class="kw">not</span> <span class="kw">in</span> VERHANDELBAR]</span>
<span id="cb4-203"><a href="#cb4-203" aria-hidden="true" tabindex="-1"></a> frei <span class="op">=</span> [i <span class="cf">for</span> i <span class="kw">in</span> alle <span class="cf">if</span> BEDINGUNGEN[i][<span class="dv">0</span>] <span class="kw">in</span> VERHANDELBAR]</span>
<span id="cb4-204"><a href="#cb4-204" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> titel, ordnung <span class="kw">in</span> ((<span class="st">&quot;unveraenderliche zuerst&quot;</span>, fest <span class="op">+</span> frei),</span>
<span id="cb4-205"><a href="#cb4-205" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;veraenderliche zuerst &quot;</span>, frei <span class="op">+</span> fest)):</span>
<span id="cb4-206"><a href="#cb4-206" aria-hidden="true" tabindex="-1"></a> konflikt <span class="op">=</span> <span class="bu">sorted</span>(deletion_filter(ordnung))</span>
<span id="cb4-207"><a href="#cb4-207" aria-hidden="true" tabindex="-1"></a> stellschrauben <span class="op">=</span> [i <span class="cf">for</span> i <span class="kw">in</span> konflikt <span class="cf">if</span> BEDINGUNGEN[i][<span class="dv">0</span>] <span class="kw">in</span> VERHANDELBAR]</span>
<span id="cb4-208"><a href="#cb4-208" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span>titel<span class="sc">}</span><span class="ss"> -&gt; </span><span class="sc">{</span><span class="bu">len</span>(konflikt)<span class="sc">}</span><span class="ss"> Bedingungen, davon &quot;</span></span>
<span id="cb4-209"><a href="#cb4-209" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="bu">len</span>(stellschrauben)<span class="sc">}</span><span class="ss"> veraenderlich&quot;</span>)</span>
<span id="cb4-210"><a href="#cb4-210" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span><span class="st">&#39;, &#39;</span><span class="sc">.</span>join(namen(konflikt))<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb4-211"><a href="#cb4-211" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">Beide Befunde sind wahr. Nur einer davon nennt dem Planer etwas,&quot;</span>)</span>
<span id="cb4-212"><a href="#cb4-212" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;das er tatsaechlich tun kann.&quot;</span>)</span>
<span id="cb4-213"><a href="#cb4-213" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-214"><a href="#cb4-214" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-215"><a href="#cb4-215" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb4-216"><a href="#cb4-216" aria-hidden="true" tabindex="-1"></a> alle <span class="op">=</span> <span class="bu">list</span>(<span class="bu">range</span>(<span class="bu">len</span>(BEDINGUNGEN)))</span>
<span id="cb4-217"><a href="#cb4-217" aria-hidden="true" tabindex="-1"></a> teil_1_der_befund(alle)</span>
<span id="cb4-218"><a href="#cb4-218" aria-hidden="true" tabindex="-1"></a> teil_2_die_naive_suche(alle)</span>
<span id="cb4-219"><a href="#cb4-219" aria-hidden="true" tabindex="-1"></a> konflikt <span class="op">=</span> teil_3_der_filter(alle)</span>
<span id="cb4-220"><a href="#cb4-220" aria-hidden="true" tabindex="-1"></a> teil_4_der_naechste_konflikt(alle, konflikt)</span>
<span id="cb4-221"><a href="#cb4-221" aria-hidden="true" tabindex="-1"></a> teil_5_die_reihenfolge(alle)</span>
<span id="cb4-222"><a href="#cb4-222" aria-hidden="true" tabindex="-1"></a> teil_6_den_befund_steuern(alle)</span>
<span id="cb4-223"><a href="#cb4-223" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span>
<span id="cb4-224"><a href="#cb4-224" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Insgesamt </span><span class="sc">{</span>aufrufe<span class="sc">}</span><span class="ss"> Solveraufrufe fuer die gesamte Diagnose.&quot;</span>)</span>
<span id="cb4-225"><a href="#cb4-225" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">68</span>)</span></code></pre></div>
<p>Die Ausgabe:</p>
<pre class="text"><code>====================================================================
1. Was der Solver sagt
====================================================================
Modell: 5 Variablen, 14 Bedingungen
Status: INFEASIBLE
Mehr ist es nicht. Der Solver nennt keine Ursache, weil es die eine
Ursache nicht gibt: Unloesbarkeit ist eine Eigenschaft von Mengen von
Bedingungen, nicht von einzelnen Bedingungen.
====================================================================
2. Die naheliegende Idee - und warum sie hier scheitert
====================================================================
Jede Bedingung einmal weglassen und schauen, ob es dann geht:
ohne Kapazitaet Montage weiter unloesbar
ohne Kapazitaet Lackieren weiter unloesbar
ohne Kapazitaet Pruefung weiter unloesbar
ohne Liefervertrag Rahmen weiter unloesbar
ohne Liefervertrag Gehaeuse weiter unloesbar
ohne Liefervertrag Deckel weiter unloesbar
ohne Liefervertrag Traeger weiter unloesbar
ohne Marktgrenze Rahmen weiter unloesbar
ohne Marktgrenze Gehaeuse weiter unloesbar
ohne Marktgrenze Deckel weiter unloesbar
ohne Marktgrenze Halter weiter unloesbar
ohne Mindestumsatz weiter unloesbar
ohne Sortimentsbreite weiter unloesbar
ohne Lackierbudget Schicht 2 weiter unloesbar
Gefundene Schuldige: 0
Keine einzelne Bedingung ist schuld. Genau das ist der Normalfall -
und der Grund, warum diese Suche in der Praxis so oft im Nichts endet.
====================================================================
3. Der Deletion Filter
====================================================================
14 Solveraufrufe -&gt; Konflikt aus 3 von 14 Bedingungen:
* Kapazitaet Lackieren
* Liefervertrag Rahmen
* Liefervertrag Gehaeuse
Probe auf Unreduzierbarkeit - jede einzelne davon weglassen:
ohne Kapazitaet Lackieren loesbar
ohne Liefervertrag Rahmen loesbar
ohne Liefervertrag Gehaeuse loesbar
Aufwand: 14 Aufrufe. Alle Teilmengen durchzuprobieren waeren 2^14 = 16.384 gewesen.
====================================================================
4. Ein Konflikt ist selten einer
====================================================================
Angenommen, &#39;Kapazitaet Lackieren&#39; laesst sich verhandeln
und wird aus dem Modell genommen. Dann ist das Modell ...
... immer noch unloesbar. Der Filter erneut:
* Kapazitaet Montage
* Liefervertrag Traeger
* Mindestumsatz
* Lackierbudget Schicht 2
Ueberschneidung mit dem ersten Konflikt: 0 Bedingungen
Ein zweiter, unabhaengiger Widerspruch, den der erste verdeckt hat.
Deshalb ist die Konfliktsuche eine Schleife, kein einzelner Aufruf:
reparieren, neu suchen, bis das Modell loesbar ist.
====================================================================
5. Es gibt nicht DEN Konflikt
====================================================================
Derselbe Filter, nur eine andere Pruefreihenfolge - 200-mal gewuerfelt:
67x 3 Bedingungen: Kapazitaet Lackieren, Liefervertrag Rahmen, Liefervertrag Gehaeuse
32x 4 Bedingungen: Kapazitaet Montage, Kapazitaet Lackieren, Marktgrenze Deckel, Mindestumsatz
20x 4 Bedingungen: Kapazitaet Montage, Liefervertrag Traeger, Mindestumsatz, Sortimentsbreite
19x 4 Bedingungen: Kapazitaet Montage, Liefervertrag Traeger, Mindestumsatz, Lackierbudget Schicht 2
19x 4 Bedingungen: Kapazitaet Montage, Liefervertrag Traeger, Marktgrenze Gehaeuse, Mindestumsatz
17x 5 Bedingungen: Kapazitaet Lackieren, Kapazitaet Pruefung, Liefervertrag Gehaeuse, Marktgrenze Deckel, Mindestumsatz
11x 4 Bedingungen: Kapazitaet Montage, Liefervertrag Deckel, Liefervertrag Traeger, Mindestumsatz
8x 4 Bedingungen: Kapazitaet Montage, Liefervertrag Rahmen, Liefervertrag Traeger, Mindestumsatz
7x 4 Bedingungen: Kapazitaet Montage, Kapazitaet Lackieren, Liefervertrag Traeger, Mindestumsatz
9 verschiedene minimale Konflikte in EINEM Modell.
Der Filter liefert *einen* kleinsten Konflikt, nicht *den* kleinsten -
den gibt es nicht. Alle oben sind gleichermassen korrekt.
====================================================================
6. Den Befund brauchbar machen
====================================================================
Der Filter wirft heraus, was er zuerst in die Hand bekommt. Wer die
unveraenderlichen Bedingungen zuerst pruefen laesst, bekommt sie eher
aus dem Befund heraus - und sieht dafuer mehr Stellschrauben.
unveraenderliche zuerst -&gt; 4 Bedingungen, davon 2 veraenderlich
Kapazitaet Montage, Kapazitaet Lackieren, Marktgrenze Deckel, Mindestumsatz
veraenderliche zuerst -&gt; 3 Bedingungen, davon 1 veraenderlich
Kapazitaet Lackieren, Liefervertrag Rahmen, Liefervertrag Gehaeuse
Beide Befunde sind wahr. Nur einer davon nennt dem Planer etwas,
das er tatsaechlich tun kann.
====================================================================
Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
====================================================================</code></pre>
<p><strong>Vier Dinge sind daran wichtig.</strong></p>
<ol type="1">
<li><strong>Die Zulässigkeitsprüfung braucht keine <a href="anhang-glossar.md#gloss:zielfunktion" class="glossar-link">Zielfunktion</a>.</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 &gt;= 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 <span class="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 <a href="anhang-glossar.md#gloss:relaxation" class="glossar-link">Relaxation</a>.</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>, <a href="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 (<a href="praxisfallen.html#sec:praxisfallen-die-fuenf-typischen-praxisfallen">Abschnitt 22.3</a>).</p>
</blockquote>
<hr />
<h2 id="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>
<ol type="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>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Diagnose: kuenstliche Schranke einziehen und sehen, wohin es laeuft</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>bounds <span class="op">=</span> [(<span class="dv">0</span>, <span class="fl">1e6</span>)] <span class="op">*</span> n</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>res <span class="op">=</span> linprog(...)</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>gross <span class="op">=</span> [j <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(n) <span class="cf">if</span> res.x[j] <span class="op">&gt;</span> <span class="fl">1e5</span>]</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f&quot;Diese Variablen laufen an die Kunstschranke: </span><span class="sc">{</span>gross<span class="sc">}</span><span class="ss">&quot;</span>)</span></code></pre></div>
<p>Die so gefundenen Variablen sind die, denen eine echte Beschränkung fehlt.</p>
<hr />
<h2 id="c3-zu-langsam">C3 — Zu langsam</h2>
<h3 id="diagnosereihenfolge">Diagnosereihenfolge</h3>
<p><strong>1. Ist es überhaupt das Lösen?</strong></p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>t0 <span class="op">=</span> time.perf_counter()<span class="op">;</span> modell_bauen()<span class="op">;</span> t1 <span class="op">=</span> time.perf_counter()</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>loesen()<span class="op">;</span> t2 <span class="op">=</span> time.perf_counter()</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f&quot;Aufbau </span><span class="sc">{</span>t1<span class="op">-</span>t0<span class="sc">:.2f}</span><span class="ss">s | Loesen </span><span class="sc">{</span>t2<span class="op">-</span>t1<span class="sc">:.2f}</span><span class="ss">s&quot;</span>)</span></code></pre></div>
<p>Bei CVXPY und bei Schleifen über Szenarien ist oft der <strong>Aufbau</strong> der Engpass, nicht der Solver (siehe <a href="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 <a href="anhang-glossar.md#gloss:gemischt-ganzzahlige-optimierung-milp" class="glossar-link">MILP</a>-Laufzeiten. Setzen Sie <span class="math inline">M</span> auf die kleinste gültige Schranke (<a href="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 <a href="anhang-glossar.md#gloss:constraint-programming-cp" class="glossar-link">CP</a>-<a href="anhang-glossar.md#gloss:sat-boolean-satisfiability" class="glossar-link">SAT</a> von Hand nachgebaut, konvexe Probleme mit <code>scipy.optimize.minimize</code> — jeweils Größenordnungen langsamer als das passende Werkzeug (<a href="oekosystem.html#sec:oekosystem-wann-lohnt-sich-welche-ebene">Abschnitt 3.6</a>).</p>
<p><strong>5. Wird vektorisiert?</strong></p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co"># langsam: # schnell:</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> s <span class="kw">in</span> <span class="bu">range</span>(S): constraints.append(u <span class="op">&gt;=</span> <span class="op">-</span>(R <span class="op">@</span> w) <span class="op">-</span> gamma)</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> constraints.append(u[s] <span class="op">&gt;=</span> <span class="op">-</span>R[s] <span class="op">@</span> w <span class="op">-</span> gamma)</span></code></pre></div>
<p><strong>6. Gap akzeptieren.</strong></p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>loeser.parameters.relative_gap_limit <span class="op">=</span> <span class="fl">0.02</span> <span class="co"># CP-SAT</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>loeser.parameters.max_time_in_seconds <span class="op">=</span> <span class="fl">30.0</span></span></code></pre></div>
<p>Bei ±10 % Datenunsicherheit sind die letzten 2 % Optimalität verlorene Zeit.</p>
<hr />
<h2 id="c4-unsinniges-ergebnis">C4 — Unsinniges Ergebnis</h2>
<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>
<h3 id="checkliste">Checkliste</h3>
<ul class="task-list">
<li><label><input type="checkbox" /><strong>Einheiten.</strong> Euro gegen Cent? Stunden gegen Minuten? Jahres- gegen Tagesgrößen? (Ein Beispiel dafür finden Sie in <a href="cvar.html#sec:cvar-implementierung-cvar-portfolio-mit-reibung">Abschnitt 20.6</a>.)</label></li>
<li><label><input type="checkbox" /><strong>Vorzeichen.</strong> Minimieren Sie, wo Sie maximieren wollten? Ist der Ertrag negativ eingetragen?</label></li>
<li><label><input type="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><input type="checkbox" /><strong>Variablenbedeutung.</strong> Ist <span class="math inline">x_{ij}</span> „Person <span class="math inline">i</span> macht Aufgabe <span class="math inline">j</span>“ oder umgekehrt? Bei asymmetrischen Matrizen fällt das nicht auf.</label></li>
<li><label><input type="checkbox" /><strong>Index-Reihenfolge.</strong> Zeilen und Spalten vertauscht? Siehe C11.</label></li>
</ul>
<h3 id="die-wirksamste-gegenmaßnahme">Die wirksamste Gegenmaßnahme</h3>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Nach JEDEM Lösen: alle Regeln explizit nachprüfen</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="cf">assert</span> <span class="bu">abs</span>(w.<span class="bu">sum</span>() <span class="op">-</span> <span class="dv">1</span>) <span class="op">&lt;</span> <span class="fl">1e-6</span>, <span class="st">&quot;Budget nicht eingehalten&quot;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="cf">assert</span> w.<span class="bu">min</span>() <span class="op">&gt;</span> <span class="op">-</span><span class="fl">1e-9</span>, <span class="st">&quot;negatives Gewicht&quot;</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="cf">assert</span> w.<span class="bu">max</span>() <span class="op">&lt;=</span> grenze <span class="op">+</span> <span class="fl">1e-9</span>, <span class="st">&quot;Positionsgrenze verletzt&quot;</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> sektor, idx <span class="kw">in</span> sektoren.items():</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> w[idx].<span class="bu">sum</span>() <span class="op">&lt;=</span> limit[sektor] <span class="op">+</span> <span class="fl">1e-9</span>, <span class="ss">f&quot;Sektor </span><span class="sc">{</span>sektor<span class="sc">}</span><span class="ss"> verletzt&quot;</span></span></code></pre></div>
<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>
<hr />
<h2 id="c5-instabile-lösung">C5 — Instabile Lösung</h2>
<p><strong>Symptom.</strong> Ein zusätzlicher Handelstag, und das Portfolio sieht völlig anders aus.</p>
<p><strong>Ursachen und Abhilfen:</strong></p>
<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 33%" />
<col style="width: 33%" />
</colgroup>
<thead>
<tr class="header">
<th>Ursache</th>
<th>Prüfung</th>
<th>Abhilfe</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Schlecht konditionierte <a href="anhang-glossar.md#gloss:kovarianzmatrix" class="glossar-link">Kovarianzmatrix</a></td>
<td><code>np.linalg.cond(Sigma)</code></td>
<td><a href="anhang-glossar.md#gloss:ledoit-wolf-shrinkage" class="glossar-link">Ledoit-Wolf-Shrinkage</a> (<a href="finanzdaten.html#kap-finanzdaten">Kapitel 18</a>)</td>
</tr>
<tr class="even">
<td>Zu wenige Beobachtungen</td>
<td><span class="math inline">T/N</span> berechnen</td>
<td>Historie verlängern oder Universum verkleinern</td>
</tr>
<tr class="odd">
<td><a href="anhang-glossar.md#gloss:alternativoptima" class="glossar-link">Alternativoptima</a></td>
<td>Zielfunktion bei mehreren Lösungen gleich?</td>
<td>Regularisierung: kleinen <span class="math inline">L_2</span>-Term addieren</td>
</tr>
<tr class="even">
<td>Fehlende Turnover-Dämpfung</td>
<td>Turnover messen</td>
<td><span class="math inline">L_1</span>-Strafe (<a href="cvar.html#sec:cvar-transaktionskosten-ueber-die-l-1-norm">Abschnitt 20.5</a>)</td>
</tr>
</tbody>
</table>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>eig <span class="op">=</span> np.linalg.eigvalsh(Sigma)</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f&quot;Kondition </span><span class="sc">{</span>eig<span class="sc">.</span><span class="bu">max</span>()<span class="op">/</span>eig<span class="sc">.</span><span class="bu">min</span>()<span class="sc">:.0f}</span><span class="ss"> | kleinster EW </span><span class="sc">{</span>eig<span class="sc">.</span><span class="bu">min</span>()<span class="sc">:.2e}</span><span class="ss">&quot;</span>)</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Faustregel: Kondition &gt; 1000 ist bedenklich, &gt; 10000 kritisch</span></span></code></pre></div>
<h3 id="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 (<span class="math inline">10^7</span>) und Tonnen-Angaben (<span class="math inline">10^{-3}</span>) in derselben Matrix erzeugen eine <a href="anhang-glossar.md#gloss:konditionszahl" class="glossar-link">Konditionszahl</a>, 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>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f&quot;Kondition der Restriktionsmatrix: </span><span class="sc">{</span>np<span class="sc">.</span>linalg<span class="sc">.</span>cond(A)<span class="sc">:.1e}</span><span class="ss">&quot;</span>)</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="co"># Grobe Peilung: Wertebereiche der Koeffizienten anschauen</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f&quot;kleinster / groesster Betrag: </span><span class="sc">{</span>np<span class="sc">.</span><span class="bu">abs</span>(A[A <span class="op">!=</span> <span class="dv">0</span>])<span class="sc">.</span><span class="bu">min</span>()<span class="sc">:.1e}</span><span class="ss"> &quot;</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;/ </span><span class="sc">{</span>np<span class="sc">.</span><span class="bu">abs</span>(A)<span class="sc">.</span><span class="bu">max</span>()<span class="sc">:.1e}</span><span class="ss">&quot;</span>)</span></code></pre></div>
<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 <a href="anhang-glossar.md#gloss:ruiz-equilibrierung" class="glossar-link">Ruiz-Equilibrierung</a> (<a href="fundament.html#sec:fundament-kondition">Abschnitt 2.7</a>).</p>
<hr />
<h2 id="c6-falsche-dualwerte">C6 — Falsche Dualwerte</h2>
<p><strong>Symptom.</strong> Schattenpreise sind negativ, wo sie positiv sein sollten — oder null bei einer offensichtlich knappen Ressource.</p>
<h3 id="die-drei-prüfungen">Die drei Prüfungen</h3>
<p><strong>1. Vorzeichenkonvention.</strong> Haben Sie zur Maximierung negiert?</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>schattenpreise <span class="op">=</span> <span class="op">-</span>res.ineqlin.marginals <span class="co"># bei linprog nach Negation</span></span></code></pre></div>
<p>Genau das ist die Vorzeichenfalle aus <a href="lp.html#sec:lp-die-vorzeichenfalle-bei-schattenpreisen">Abschnitt 5.7</a>.</p>
<p><strong>2. <a href="anhang-glossar.md#gloss:komplementaerer-schlupf" class="glossar-link">Komplementärer Schlupf</a>.</strong></p>
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> s, y <span class="kw">in</span> <span class="bu">zip</span>(res.slack, schattenpreise):</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> <span class="bu">abs</span>(s <span class="op">*</span> y) <span class="op">&lt;</span> <span class="fl">1e-6</span>, <span class="st">&quot;Komplementaerer Schlupf verletzt!&quot;</span></span></code></pre></div>
<p>Eine Ressource mit Reserve <strong>muss</strong> <a href="anhang-glossar.md#gloss:schattenpreis" class="glossar-link">Schattenpreis</a> 0 haben.</p>
<p><strong>3. Numerische Gegenprobe — der zuverlässigste Test.</strong></p>
<div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>b_plus <span class="op">=</span> b.copy()<span class="op">;</span> b_plus[i] <span class="op">+=</span> <span class="fl">1.0</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>zuwachs <span class="op">=</span> loese(b_plus) <span class="op">-</span> loese(b)</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="cf">assert</span> <span class="bu">abs</span>(zuwachs <span class="op">-</span> schattenpreise[i]) <span class="op">&lt;</span> <span class="fl">1e-6</span></span></code></pre></div>
<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: <a href="anhang-glossar.md#gloss:entartung" class="glossar-link">Entartung</a>.</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>
<div class="sourceCode" id="cb16"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>aktiv <span class="op">=</span> <span class="bu">int</span>(np.<span class="bu">sum</span>(res.slack <span class="op">&lt;</span> <span class="fl">1e-9</span>)) <span class="co"># wie viele Bedingungen sind straff?</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> aktiv <span class="op">&gt;</span> <span class="bu">len</span>(res.x):</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Entartet: </span><span class="sc">{</span>aktiv<span class="sc">}</span><span class="ss"> aktive Bedingungen bei </span><span class="sc">{</span><span class="bu">len</span>(res.x)<span class="sc">}</span><span class="ss"> Variablen.&quot;</span>)</span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Einen einzelnen Schattenpreis zu berichten waere hier irrefuehrend.&quot;</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 (<a href="lp.html#sec:lp-entartung">Abschnitt 5.9</a>).</p>
<hr />
<h2 id="c7-widersprüchliche-solver">C7 — Widersprüchliche Solver</h2>
<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 <span class="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 />
<h2 id="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 <a href="anhang-glossar.md#gloss:konvexitaet" class="glossar-link">Konvexität</a> Ihres Ausdrucks nicht beweisen — es lehnt ab, statt ein Ergebnis ohne Garantie zu liefern. <strong>Das ist ein Feature.</strong></p>
<table>
<colgroup>
<col style="width: 44%" />
<col style="width: 20%" />
<col style="width: 35%" />
</colgroup>
<thead>
<tr class="header">
<th>Problematischer Ausdruck</th>
<th>Warum</th>
<th>Lösung</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>x * y</code> (beide Variablen)</td>
<td>bilinear</td>
<td>umformulieren oder MILP/<a href="anhang-glossar.md#gloss:nichtlineare-programmierung-nlp" class="glossar-link">NLP</a></td>
</tr>
<tr class="even">
<td><code>a / x</code></td>
<td>nicht konvex</td>
<td><code>cp.inv_pos(x)</code> bei <span class="math inline">x&gt;0</span></td>
</tr>
<tr class="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 <a href="anhang-glossar.md#gloss:dcp-disciplined-convex-programming" class="glossar-link">DCP</a>, egal wie sauber <span class="math inline">S</span> ist</td>
<td>Cholesky <span class="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>
<tr class="even">
<td><code>cp.quad_form(w, S)</code> bei numerisch unsauberem <span class="math inline">S</span></td>
<td>Eigenwerte knapp unter null durch Rundung — CVXPY erkennt <span class="math inline">S</span> nicht als PSD</td>
<td><code>cp.psd_wrap(S)</code>, <strong>wenn</strong> <span class="math inline">S</span> nachweislich PSD ist (z. B. eine Kovarianzmatrix). Behebt nur diese numerische Beanstandung, keine Regelverletzung im Aufbau</td>
</tr>
<tr class="odd">
<td>Quotient (z. B. <a href="anhang-glossar.md#gloss:sharpe-ratio" class="glossar-link">Sharpe Ratio</a>)</td>
<td>nicht konvex</td>
<td><a href="anhang-glossar.md#gloss:korn-transformation" class="glossar-link">Korn-Transformation</a> (<a href="markowitz.html#sec:markowitz-das-mean-variance-modell">Abschnitt 19.4</a>)</td>
</tr>
<tr class="even">
<td><code>cp.log(x)</code> maximieren</td>
<td>konkav — das ist <strong>erlaubt</strong></td>
<td>in <code>cp.Maximize</code> verwenden</td>
</tr>
</tbody>
</table>
<div class="sourceCode" id="cb18"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(problem.is_dcp()) <span class="co"># False?</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> c <span class="kw">in</span> problem.constraints:</span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(c.is_dcp(), c) <span class="co"># zeigt die Schuldige</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(problem.objective.is_dcp())</span></code></pre></div>
<hr />
<h2 id="c9-importfehler">C9 — Importfehler</h2>
<pre><code>ImportError: .../highspy/_core...so: undefined symbol: _ZN5Highs13releaseMemoryEv</code></pre>
<p><strong>Ursache.</strong> <code>ortools</code> und <code>highspy</code> bringen beide eine eigene <a href="anhang-glossar.md#gloss:highs" class="glossar-link">HiGHS</a>-Kopie mit; sie lassen sich auf vielen Systemen <strong>nicht im selben Prozess</strong> importieren (siehe <a href="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>
<hr />
<h2 id="c10-verdächtig-guter-backtest">C10 — Verdächtig guter Backtest</h2>
<p><strong>Faustregel:</strong> Eine Sharpe Ratio über 2 bei einer einfachen Strategie ist fast immer ein Fehler, kein Fund.</p>
<h3 id="prüfreihenfolge">Prüfreihenfolge</h3>
<p><strong>1. Lookahead-Selbsttest.</strong></p>
<div class="sourceCode" id="cb20"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a>signal_vorher <span class="op">=</span> berechne_signal(stichtag)</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>kurse.loc[kurse.index <span class="op">&gt;</span> stichtag] <span class="op">*=</span> <span class="fl">3.0</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>signal_nachher <span class="op">=</span> berechne_signal(stichtag)</span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a><span class="cf">assert</span> np.allclose(signal_vorher, signal_nachher), <span class="st">&quot;LOOKAHEAD-BIAS!&quot;</span></span></code></pre></div>
<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 (<a href="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>
<hr />
<h2 id="c11-vertauschte-spalten">C11 — Vertauschte Spalten</h2>
<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>
<div class="sourceCode" id="cb21"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="co"># IMMER nach dem Import:</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>kurse <span class="op">=</span> roh[<span class="st">&quot;Close&quot;</span>][tickers].dropna() <span class="co"># erzwingt die Reihenfolge</span></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a><span class="cf">assert</span> <span class="bu">list</span>(kurse.columns) <span class="op">==</span> tickers, (</span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Reihenfolge weicht ab!</span><span class="ch">\n</span><span class="ss"> erwartet: </span><span class="sc">{</span>tickers<span class="sc">}</span><span class="ch">\n</span><span class="ss"> erhalten: </span><span class="sc">{</span><span class="bu">list</span>(kurse.columns)<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Und Gruppen NIE ueber Positionsindizes definieren:</span></span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a>SEKTOREN <span class="op">=</span> {<span class="st">&quot;Technologie&quot;</span>: [<span class="st">&quot;AAPL&quot;</span>, <span class="st">&quot;MSFT&quot;</span>, <span class="st">&quot;NVDA&quot;</span>, <span class="st">&quot;AMZN&quot;</span>]} <span class="co"># gut</span></span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a>tech_idx <span class="op">=</span> [tickers.index(t) <span class="cf">for</span> t <span class="kw">in</span> SEKTOREN[<span class="st">&quot;Technologie&quot;</span>]] <span class="co"># bricht bei Tippfehler</span></span>
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a>tech_indices <span class="op">=</span> [<span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>] <span class="co"># SCHLECHT</span></span></code></pre></div>
<hr />
<h2 id="die-goldene-diagnoseregel">Die goldene Diagnoseregel</h2>
<blockquote>
<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>
</blockquote>
</article>
<button type="button" class="fortschritt-knopf" data-kapitel="anhang-fehlerdiagnose.html"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg> <span>Als gelesen markieren</span></button>
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="anhang-modellierungsmuster.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Anhang B: Katalog der Modellierungsmuster</span></a><a class="prev-next-knopf prev-next-next" href="anhang-spickzettel.html"><span><small>Weiter</small>Anhang D: Spickzettel der Solver</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
</main>
</div>
<footer class="site-footer">
<p>© Dieter Schlüter · <a href="gesamtdokument.html">Gesamtdokument</a> ·
<a href="programme.html">Beispielprogramme</a></p>
</footer>
<script defer src="assets/search-index.js"></script>
<script defer src="assets/programme.js"></script>
<script defer src="assets/site.js"></script>
</body>
</html>