Setzt Paket 5 aus Verbesserungen_02.md um (den Teil, der nicht zurueckgestellt wurde). Fuenf neue Dateien 19_/29_/39_/49_/52_Synthese_*.md, je eine am Ende eines Teils, mit eigener Website-Seite ueber SONDERSEITEN - sie tragen bewusst keine "# Kapitel:"-Ueberschrift, weil sie keine Kapitel sind, sondern der Rueckblick auf einen Teil. Der Entwurf musste sich abgrenzen: Die Teil-Einleitungen haben bereits Entscheidungsdiagramme. Eine zweite Matrix am Teil-Ende waere eine Dopplung gewesen. Die Synthesen leisten deshalb, was eine Einleitung nicht kann - den Vergleich ueber die Kapitel hinweg (Verfahren nebeneinander, mit der Spalte "wo es aufhoert"), eine Tabelle "was dieser Teil gemessen hat" (Behauptung gegen Messung gegen Fundstelle) und drei Fehler, die der Teil verhindert. Zitiert wird ausschliesslich, was im Buch tatsaechlich gerechnet wird. Drei Funde beim Einbau: * Teil III sagte "die drei Kapitel dieses Teils", hat aber fuenf. Phase 3 hatte Mehrziel und Predict-then-Optimize hinzugefuegt, die Einleitung blieb stehen. * 50_Praxis.md verwies auf die Projektwerkstatt mit "acht eigene Anwendungen" - sie hat elf. * Und der eigentliche Fund: Die Lesekette der Quelldateien fuehrte an ACHT Kapiteln vorbei. 12_Python_Oekosystem zeigte direkt auf 20_Lineare_Programmierung, 23_Graphen direkt auf 30_QP, 32_Dynamische direkt auf 40_Finanzdaten, 50_Praxis direkt auf die Projektwerkstatt. Wer der Kette folgte, uebersprang acht von 23 Kapiteln - darunter Metaheuristiken, Spaltengenerierung, Strukturbruecke, Supply-Chain und das ganze Testing-Kapitel. Zehn weitere Dateien hatten gar keine Navigationszeile. Zur Reichweite, damit sie nicht ueberschaetzt wird: Diese Zeilen stehen nur in den Quelldateien. entferne_navigation() streicht sie aus dem Gesamtdokument, und die Website baut ihre Vor/Zurueck-Knoepfe selbst aus DATEIEN. PDF und Website waren nie betroffen - wohl aber jeder, der die Markdown-Dateien im Repository liest, und das wird nach der Veroeffentlichung der Normalfall sein. Die Kette ist jetzt ueber alle 35 Uebergaenge geschlossen, und --check bewacht sie: Fehlt eine Zeile oder zeigt sie an der in DATEIEN folgenden Datei vorbei, ist der Lauf rot. Gegengetestet mit beiden Bruchformen. Stand: 36 Dateien, 296 Abschnitte, 815 Querverweise, 328 Indexmarken, 76 Programme (unveraendert), 33 pytest-Tests, PDF 758 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
420 lines
73 KiB
HTML
Generated
420 lines
73 KiB
HTML
Generated
<!doctype html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Anhang D: Spickzettel der Solver · 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</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</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 — 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 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 — 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</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"><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" class="aktiv"><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-literatur.html"><a href="anhang-glossar-literatur.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang E: Glossar und Literatur</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="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 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 als PDF</a></li></ul></div></nav>
|
||
<main class="content">
|
||
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="index.html">Start</a> › <span>Anhang D: Spickzettel der Solver</span></nav>
|
||
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="anhang-fehlerdiagnose.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Anhang C: Fehlerdiagnose-Handbuch</span></a><a class="prev-next-knopf prev-next-next" href="anhang-glossar-literatur.html"><span><small>Weiter</small>Anhang E: Glossar und Literatur</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
|
||
<article>
|
||
<h1 id="anhang-spickzettel">Anhang D: Spickzettel der Solver</h1>
|
||
<blockquote>
|
||
<p><strong>Wofür dieser Anhang gedacht ist:</strong> Sie wissen, was Sie modellieren wollen, und suchen nur noch, wie die gewählte Bibliothek es schreibt. Jede Seite hat denselben Aufbau — Modell, Variablen, Nebenbedingungen, Lösen, <strong>alle</strong> Statusfälle, Lösung auslesen, Stolpersteine. So lassen sich die Seiten nebeneinanderlegen.</p>
|
||
</blockquote>
|
||
<p><strong>Alle Schnipsel lösen dasselbe Problem</strong> — das Produktionsprogramm aus <a href="oekosystem.html#sec:oekosystem-schnellstart">Abschnitt 3.1</a> mit dem bekannten Optimum <strong>530</strong> und den Schattenpreisen <strong>12</strong> und <strong>1</strong>:</p>
|
||
<p><span class="math display">\max\; 10x_1 + 15x_2 + 25x_3 \quad\text{u.d.N.}\quad x_1 + x_2 + 2x_3 \le 40,\;\; 2x_1 + 3x_2 + x_3 \le 50,\;\; x \ge 0</span></p>
|
||
<p>Damit ist jeder Schnipsel selbstprüfend: Kommt bei Ihnen etwas anderes als 530 heraus, liegt es an der Übertragung, nicht am Modell. (Die Ausnahme ist CP-SAT — ein rein stetiges LP ist dort das falsche Werkzeug; die Seite zeigt stattdessen die CP-SAT-eigenen Bausteine.)</p>
|
||
<blockquote>
|
||
<p><strong>Was dieser Anhang <em>nicht</em> ist.</strong> Kein Vergleich und keine Empfehlung. Welche Bibliothek für welche Aufgabe taugt, steht in <a href="oekosystem.html#sec:oekosystem-wann-lohnt-sich-welche-ebene">Abschnitt 3.6</a>; denselben Fall in vier Bibliotheken <em>nebeneinander</em> zeigt <code>Ein_System_Vier_Ansaetze.py</code>, die beiden Modellierungssprachen <code>Modellierungsschichten.py</code>. Hier geht es allein ums Nachschlagen.</p>
|
||
</blockquote>
|
||
<hr />
|
||
<h2 id="d1-scipy-linprog-und-milp">D1 — SciPy: <code>linprog</code> und <code>milp</code></h2>
|
||
<p><strong>Wofür.</strong> Die Einstiegsschicht: keine zusätzliche Installation, HiGHS als Unterbau, ideal für lineare und gemischt-ganzzahlige Probleme in Matrixform. <strong>Wofür nicht:</strong> alles, was sich nicht als Matrix schreiben lässt, und jede nichtlineare Zielfunktion.</p>
|
||
<h3 id="lineares-programm">Lineares Programm</h3>
|
||
<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="im">import</span> numpy <span class="im">as</span> np</span>
|
||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> scipy.optimize <span class="im">import</span> linprog</span>
|
||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co"># linprog MINIMIERT immer -> zum Maximieren die Zielfunktion negieren</span></span>
|
||
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>ergebnis <span class="op">=</span> linprog(</span>
|
||
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> c<span class="op">=</span>[<span class="op">-</span><span class="fl">10.0</span>, <span class="op">-</span><span class="fl">15.0</span>, <span class="op">-</span><span class="fl">25.0</span>], <span class="co"># Zielkoeffizienten</span></span>
|
||
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> A_ub<span class="op">=</span>[[<span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">2</span>], [<span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">1</span>]], b_ub<span class="op">=</span>[<span class="dv">40</span>, <span class="dv">50</span>], <span class="co"># A_ub @ x <= b_ub</span></span>
|
||
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> A_eq<span class="op">=</span><span class="va">None</span>, b_eq<span class="op">=</span><span class="va">None</span>, <span class="co"># Gleichungen, falls vorhanden</span></span>
|
||
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> bounds<span class="op">=</span>[(<span class="dv">0</span>, <span class="va">None</span>)] <span class="op">*</span> <span class="dv">3</span>, <span class="co"># je Variable (unten, oben)</span></span>
|
||
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> method<span class="op">=</span><span class="st">"highs"</span>)</span>
|
||
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> ergebnis.status <span class="op">==</span> <span class="dv">0</span>:</span>
|
||
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"optimal: </span><span class="sc">{</span><span class="op">-</span>ergebnis<span class="sc">.</span>fun<span class="sc">:.2f}</span><span class="ss">"</span>) <span class="co"># Vorzeichen zuruecknehmen!</span></span>
|
||
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"x = </span><span class="sc">{</span>np<span class="sc">.</span><span class="bu">round</span>(ergebnis.x, <span class="dv">4</span>)<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Schattenpreise: </span><span class="sc">{</span><span class="op">-</span>ergebnis<span class="sc">.</span>ineqlin<span class="sc">.</span>marginals<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> ergebnis.status <span class="op">==</span> <span class="dv">2</span>:</span>
|
||
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"INFEASIBLE - kein zulaessiger Punkt"</span>)</span>
|
||
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> ergebnis.status <span class="op">==</span> <span class="dv">3</span>:</span>
|
||
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"UNBOUNDED - Zielfunktion unbeschraenkt"</span>)</span>
|
||
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> ergebnis.status <span class="op">==</span> <span class="dv">1</span>:</span>
|
||
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Iterations- oder Zeitlimit erreicht"</span>)</span>
|
||
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span>:</span>
|
||
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"numerisches Problem (status </span><span class="sc">{</span>ergebnis<span class="sc">.</span>status<span class="sc">}</span><span class="ss">): </span><span class="sc">{</span>ergebnis<span class="sc">.</span>message<span class="sc">}</span><span class="ss">"</span>)</span></code></pre></div>
|
||
<h3 id="ganzzahlig-milp">Ganzzahlig: <code>milp</code></h3>
|
||
<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="im">import</span> numpy <span class="im">as</span> np</span>
|
||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> scipy.optimize <span class="im">import</span> milp, LinearConstraint, Bounds</span>
|
||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Ganzzahlig: dasselbe Problem, aber x muss ganzzahlig sein</span></span>
|
||
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>ergebnis <span class="op">=</span> milp(</span>
|
||
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> c<span class="op">=</span>[<span class="op">-</span><span class="fl">10.0</span>, <span class="op">-</span><span class="fl">15.0</span>, <span class="op">-</span><span class="fl">25.0</span>], <span class="co"># auch milp MINIMIERT</span></span>
|
||
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> constraints<span class="op">=</span>LinearConstraint([[<span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">2</span>], [<span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">1</span>]], <span class="op">-</span>np.inf, [<span class="dv">40</span>, <span class="dv">50</span>]),</span>
|
||
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> integrality<span class="op">=</span>[<span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>], <span class="co"># 0 = stetig, 1 = ganzzahlig</span></span>
|
||
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> bounds<span class="op">=</span>Bounds(<span class="dv">0</span>, np.inf))</span>
|
||
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> ergebnis.status <span class="op">==</span> <span class="dv">0</span>:</span>
|
||
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"optimal: </span><span class="sc">{</span><span class="op">-</span>ergebnis<span class="sc">.</span>fun<span class="sc">:.2f}</span><span class="ss"> x = </span><span class="sc">{</span>np<span class="sc">.</span><span class="bu">round</span>(ergebnis.x)<span class="sc">.</span>astype(<span class="bu">int</span>)<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"MIP-Gap: </span><span class="sc">{</span>ergebnis<span class="sc">.</span>mip_gap<span class="sc">:.4f}</span><span class="ss">"</span>)</span>
|
||
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> ergebnis.status <span class="op">==</span> <span class="dv">1</span>:</span>
|
||
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Zeitlimit - beste gefundene Loesung nutzen, Gap pruefen"</span>)</span>
|
||
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> ergebnis.status <span class="op">==</span> <span class="dv">2</span>:</span>
|
||
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"INFEASIBLE"</span>)</span>
|
||
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> ergebnis.status <span class="op">==</span> <span class="dv">3</span>:</span>
|
||
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"UNBOUNDED"</span>)</span>
|
||
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span>:</span>
|
||
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"kein Ergebnis: </span><span class="sc">{</span>ergebnis<span class="sc">.</span>message<span class="sc">}</span><span class="ss">"</span>)</span></code></pre></div>
|
||
<h3 id="die-drei-häufigsten-stolpersteine">Die drei häufigsten Stolpersteine</h3>
|
||
<ol type="1">
|
||
<li><strong><code>linprog</code> minimiert immer.</strong> Zum Maximieren <code>c</code> negieren — und beim Ausgeben des Zielwerts das Vorzeichen wieder zurücknehmen. Dieselbe Negation dreht auch die Schattenpreise (<a href="lp.html#sec:lp-die-vorzeichenfalle-bei-schattenpreisen">Abschnitt 5.7</a>).</li>
|
||
<li><strong><code>status == 0</code> prüfen, nicht <code>res.success</code> allein.</strong> <code>success</code> ist bei <code>status == 1</code> (Limit erreicht) <code>False</code>, obwohl eine brauchbare Lösung vorliegen kann.</li>
|
||
<li><strong><code>bounds</code> gilt je Variable.</strong> <code>bounds=(0, None)</code> setzt alle Variablen gleich; <code>bounds=[(0, None), (0, 10), ...]</code> einzeln. Wer die Liste vergisst, bekommt stillschweigend überall dieselbe Schranke.</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="d2-highs-über-highspy">D2 — HiGHS über <code>highspy</code></h2>
|
||
<p><strong>Wofür.</strong> Derselbe Solver wie unter SciPy, aber direkt gesteuert: Optionen, Warm-Starts, inkrementelles Ändern eines bestehenden Modells. <strong>Wofür nicht:</strong> schnelles Hinschreiben — die CSR-Matrixübergabe ist fehleranfällig.</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="im">import</span> numpy <span class="im">as</span> np</span>
|
||
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> highspy</span>
|
||
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>h <span class="op">=</span> highspy.Highs()</span>
|
||
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>h.setOptionValue(<span class="st">"output_flag"</span>, <span class="va">False</span>) <span class="co"># Solverprotokoll abschalten</span></span>
|
||
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>h.setOptionValue(<span class="st">"time_limit"</span>, <span class="fl">60.0</span>)</span>
|
||
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>h.setOptionValue(<span class="st">"mip_rel_gap"</span>, <span class="fl">0.01</span>) <span class="co"># 1 % Gap genuegt</span></span>
|
||
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="co"># Variablen: Anzahl, Untergrenzen, Obergrenzen</span></span>
|
||
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>h.addVars(<span class="dv">3</span>, np.zeros(<span class="dv">3</span>), np.full(<span class="dv">3</span>, highspy.kHighsInf))</span>
|
||
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>h.changeObjectiveSense(highspy.ObjSense.kMaximize)</span>
|
||
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> spalte, wert <span class="kw">in</span> <span class="bu">enumerate</span>([<span class="fl">10.0</span>, <span class="fl">15.0</span>, <span class="fl">25.0</span>]):</span>
|
||
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a> h.changeColCost(spalte, wert)</span>
|
||
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a><span class="co"># Zeilen im CSR-Format: starts[i] = Beginn von Zeile i in indices/values</span></span>
|
||
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a>h.addRows(<span class="dv">2</span>, np.full(<span class="dv">2</span>, <span class="op">-</span>highspy.kHighsInf), np.array([<span class="fl">40.0</span>, <span class="fl">50.0</span>]), <span class="dv">6</span>,</span>
|
||
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a> np.array([<span class="dv">0</span>, <span class="dv">3</span>], dtype<span class="op">=</span>np.int32), <span class="co"># starts</span></span>
|
||
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a> np.array([<span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">2</span>], dtype<span class="op">=</span>np.int32), <span class="co"># Spaltenindizes</span></span>
|
||
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a> np.array([<span class="fl">1.0</span>, <span class="fl">1.0</span>, <span class="fl">2.0</span>, <span class="fl">2.0</span>, <span class="fl">3.0</span>, <span class="fl">1.0</span>])) <span class="co"># Koeffizienten</span></span>
|
||
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a><span class="co"># Ganzzahligkeit: h.changeColsIntegrality(...) mit highspy.HighsVarType.kInteger</span></span>
|
||
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a>h.run()</span>
|
||
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a>status <span class="op">=</span> h.getModelStatus()</span>
|
||
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> status <span class="op">==</span> highspy.HighsModelStatus.kOptimal:</span>
|
||
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"optimal: </span><span class="sc">{</span>h<span class="sc">.</span>getInfo()<span class="sc">.</span>objective_function_value<span class="sc">:.2f}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"x = </span><span class="sc">{</span>np<span class="sc">.</span><span class="bu">round</span>(h.getSolution().col_value[:<span class="dv">3</span>], <span class="dv">4</span>)<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Schattenpreise: </span><span class="sc">{</span>np<span class="sc">.</span><span class="bu">round</span>(h.getSolution().row_dual[:<span class="dv">2</span>], <span class="dv">4</span>)<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> status <span class="op">==</span> highspy.HighsModelStatus.kInfeasible:</span>
|
||
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"INFEASIBLE"</span>)</span>
|
||
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> status <span class="op">==</span> highspy.HighsModelStatus.kUnbounded:</span>
|
||
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"UNBOUNDED"</span>)</span>
|
||
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> status <span class="op">==</span> highspy.HighsModelStatus.kTimeLimit:</span>
|
||
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Zeitlimit, Gap </span><span class="sc">{</span>h<span class="sc">.</span>getInfo()<span class="sc">.</span>mip_gap<span class="sc">:.3f}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span>:</span>
|
||
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"kein Optimum:"</span>, h.modelStatusToString(status))</span></code></pre></div>
|
||
<h3 id="die-drei-häufigsten-stolpersteine-1">Die drei häufigsten Stolpersteine</h3>
|
||
<ol type="1">
|
||
<li><strong>Nicht zusammen mit <code>ortools</code> importieren.</strong> Beide bringen eine eigene HiGHS-Kopie mit; im selben Prozess endet das in <code>undefined symbol</code> (<a href="anhang-fehlerdiagnose.html#anhang-fehlerdiagnose">Anhang C</a>, C9). Auch <code>cvxpy</code> zieht <code>highspy</code> bei der Solver-Erkennung mit hinein.</li>
|
||
<li><strong>Das CSR-Format stimmt oder es stimmt still nicht.</strong> <code>starts</code> hat so viele Einträge wie Zeilen, <code>indices</code> und <code>values</code> so viele wie Nichtnullen. Ein falscher <code>starts</code>-Eintrag erzeugt ein <em>anderes</em>, aber lösbares Modell — es fällt nur durch ein falsches Ergebnis auf.</li>
|
||
<li><strong><code>output_flag</code> abschalten</strong>, sonst überschwemmt das Solverprotokoll jede Ausgabe.</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="d3-or-tools-pywraplp">D3 — OR-Tools: <code>pywraplp</code></h2>
|
||
<p><strong>Wofür.</strong> Bequeme algebraische Schreibweise für LP und MILP mit umschaltbarem Backend (<code>GLOP</code>, <code>SCIP</code>, <code>CBC</code>, <code>SAT</code>). <strong>Wofür nicht:</strong> Scheduling und kombinatorische Bedingungen — dafür ist CP-SAT (D4) da.</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="im">from</span> ortools.linear_solver <span class="im">import</span> pywraplp</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"># "GLOP" = LP, "SCIP" oder "CBC" = MILP, "SAT" = CP-SAT als MILP-Backend</span></span>
|
||
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>loeser <span class="op">=</span> pywraplp.Solver.CreateSolver(<span class="st">"GLOP"</span>)</span>
|
||
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> loeser <span class="kw">is</span> <span class="va">None</span>:</span>
|
||
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">SystemExit</span>(<span class="st">"Solver nicht verfuegbar"</span>)</span>
|
||
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>loeser.SetTimeLimit(<span class="dv">60_000</span>) <span class="co"># Millisekunden!</span></span>
|
||
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>unendlich <span class="op">=</span> loeser.infinity()</span>
|
||
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>x <span class="op">=</span> [loeser.NumVar(<span class="dv">0</span>, unendlich, <span class="ss">f"x</span><span class="sc">{</span>j<span class="sc">}</span><span class="ss">"</span>) <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">3</span>)]</span>
|
||
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a><span class="co"># ganzzahlig: loeser.IntVar(0, unendlich, "n") | binaer: loeser.BoolVar("b")</span></span>
|
||
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>loeser.Add(x[<span class="dv">0</span>] <span class="op">+</span> x[<span class="dv">1</span>] <span class="op">+</span> <span class="dv">2</span> <span class="op">*</span> x[<span class="dv">2</span>] <span class="op"><=</span> <span class="dv">40</span>)</span>
|
||
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>loeser.Add(<span class="dv">2</span> <span class="op">*</span> x[<span class="dv">0</span>] <span class="op">+</span> <span class="dv">3</span> <span class="op">*</span> x[<span class="dv">1</span>] <span class="op">+</span> x[<span class="dv">2</span>] <span class="op"><=</span> <span class="dv">50</span>)</span>
|
||
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>loeser.Maximize(<span class="dv">10</span> <span class="op">*</span> x[<span class="dv">0</span>] <span class="op">+</span> <span class="dv">15</span> <span class="op">*</span> x[<span class="dv">1</span>] <span class="op">+</span> <span class="dv">25</span> <span class="op">*</span> x[<span class="dv">2</span>])</span>
|
||
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a>status <span class="op">=</span> loeser.Solve()</span>
|
||
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> status <span class="op">==</span> pywraplp.Solver.OPTIMAL:</span>
|
||
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"optimal: </span><span class="sc">{</span>loeser<span class="sc">.</span>Objective()<span class="sc">.</span>Value()<span class="sc">:.2f}</span><span class="ss">"</span>)</span>
|
||
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"x = </span><span class="sc">{</span>[<span class="bu">round</span>(v.solution_value(), <span class="dv">4</span>) <span class="cf">for</span> v <span class="kw">in</span> x]<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> status <span class="op">==</span> pywraplp.Solver.FEASIBLE:</span>
|
||
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"zulaessig, nicht bewiesen optimal (Zeitlimit)"</span>)</span>
|
||
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> status <span class="op">==</span> pywraplp.Solver.INFEASIBLE:</span>
|
||
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"INFEASIBLE"</span>)</span>
|
||
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> status <span class="op">==</span> pywraplp.Solver.UNBOUNDED:</span>
|
||
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"UNBOUNDED"</span>)</span>
|
||
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span>:</span>
|
||
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"ABNORMAL / NOT_SOLVED - Modell oder Solver pruefen"</span>)</span></code></pre></div>
|
||
<h3 id="die-drei-häufigsten-stolpersteine-2">Die drei häufigsten Stolpersteine</h3>
|
||
<ol type="1">
|
||
<li><strong><code>CreateSolver</code> liefert <code>None</code></strong>, wenn der Backend-Name unbekannt oder nicht gebaut ist — immer prüfen, statt am <code>None</code> später zu scheitern.</li>
|
||
<li><strong><code>SetTimeLimit</code> erwartet Millisekunden</strong>, nicht Sekunden. Ein <code>SetTimeLimit(60)</code> bricht nach einer sechzigstel Sekunde ab.</li>
|
||
<li><strong><code>FEASIBLE</code> ist kein <code>OPTIMAL</code>.</strong> Bei Zeitlimit liefert der Solver eine gültige, aber möglicherweise schlechte Lösung — den Gap mitberichten, nicht die Zahl allein.</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="d4-cp-sat-cp_model">D4 — CP-SAT: <code>cp_model</code></h2>
|
||
<p><strong>Wofür.</strong> Scheduling, Zuordnung, Reihenfolgen, alles Kombinatorische mit globalen Bedingungen. <strong>Wofür nicht:</strong> stetige Größen — CP-SAT rechnet ausschließlich ganzzahlig. Wer Nachkommastellen braucht, skaliert (Cent statt Euro, Promille statt Anteil).</p>
|
||
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> ortools.sat.python <span class="im">import</span> cp_model</span>
|
||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>modell <span class="op">=</span> cp_model.CpModel()</span>
|
||
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="co"># Variablen - CP-SAT rechnet ausschliesslich mit GANZEN Zahlen</span></span>
|
||
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>x <span class="op">=</span> modell.NewIntVar(<span class="dv">0</span>, <span class="dv">100</span>, <span class="st">"x"</span>) <span class="co"># untere, obere Schranke, Name</span></span>
|
||
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>y <span class="op">=</span> modell.NewIntVar(<span class="dv">0</span>, <span class="dv">100</span>, <span class="st">"y"</span>)</span>
|
||
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>b <span class="op">=</span> modell.NewBoolVar(<span class="st">"b"</span>) <span class="co"># 0/1</span></span>
|
||
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="co"># Nebenbedingungen</span></span>
|
||
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>modell.Add(<span class="dv">2</span> <span class="op">*</span> x <span class="op">+</span> <span class="dv">3</span> <span class="op">*</span> y <span class="op"><=</span> <span class="dv">50</span>)</span>
|
||
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>modell.Add(x <span class="op">>=</span> <span class="dv">5</span>).OnlyEnforceIf(b) <span class="co"># gilt nur, wenn b wahr ist</span></span>
|
||
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>modell.AddAllDifferent([x, y]) <span class="co"># globale Bedingung</span></span>
|
||
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>modell.AddMaxEquality(z <span class="op">:=</span> modell.NewIntVar(<span class="dv">0</span>, <span class="dv">100</span>, <span class="st">"z"</span>), [x, y])</span>
|
||
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>modell.Maximize(<span class="dv">10</span> <span class="op">*</span> x <span class="op">+</span> <span class="dv">15</span> <span class="op">*</span> y <span class="op">-</span> <span class="dv">3</span> <span class="op">*</span> z)</span>
|
||
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>loeser <span class="op">=</span> cp_model.CpSolver()</span>
|
||
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>loeser.parameters.max_time_in_seconds <span class="op">=</span> <span class="fl">10.0</span></span>
|
||
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a>loeser.parameters.num_workers <span class="op">=</span> <span class="dv">1</span> <span class="co"># 1 = reproduzierbar</span></span>
|
||
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a>loeser.parameters.random_seed <span class="op">=</span> <span class="dv">1</span></span>
|
||
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a>status <span class="op">=</span> loeser.Solve(modell)</span>
|
||
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> status <span class="op">==</span> cp_model.OPTIMAL:</span>
|
||
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"optimal: </span><span class="sc">{</span>loeser<span class="sc">.</span>ObjectiveValue()<span class="sc">:.0f}</span><span class="ss"> x=</span><span class="sc">{</span>loeser<span class="sc">.</span>Value(x)<span class="sc">}</span><span class="ss"> y=</span><span class="sc">{</span>loeser<span class="sc">.</span>Value(y)<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> status <span class="op">==</span> cp_model.FEASIBLE:</span>
|
||
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"zulaessig, nicht bewiesen optimal - Gap-Schranke: </span><span class="sc">{</span>loeser<span class="sc">.</span>BestObjectiveBound()<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> status <span class="op">==</span> cp_model.INFEASIBLE:</span>
|
||
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"INFEASIBLE - Bedingungen widersprechen sich"</span>)</span>
|
||
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> status <span class="op">==</span> cp_model.MODEL_INVALID:</span>
|
||
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Modellfehler:"</span>, modell.Validate())</span>
|
||
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span>:</span>
|
||
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"UNKNOWN - Zeit abgelaufen, ohne eine Loesung zu finden"</span>)</span>
|
||
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f"Laufzeit </span><span class="sc">{</span>loeser<span class="sc">.</span>WallTime()<span class="sc">:.3f}</span><span class="ss">s, </span><span class="sc">{</span>loeser<span class="sc">.</span>NumBranches()<span class="sc">}</span><span class="ss"> Verzweigungen"</span>)</span></code></pre></div>
|
||
<h3 id="die-drei-häufigsten-stolpersteine-3">Die drei häufigsten Stolpersteine</h3>
|
||
<ol type="1">
|
||
<li><strong>Alles ist ganzzahlig.</strong> <code>0.5 * x</code> gibt es nicht. Skalieren Sie das ganze Modell auf eine feinere Einheit, statt zu runden.</li>
|
||
<li><strong><code>UNKNOWN</code> heißt nicht <code>INFEASIBLE</code>.</strong> Es heißt: Die Zeit war zu knapp, um überhaupt etwas zu finden. Die beiden zu verwechseln ist einer der teuersten Fehler in Produktion — das Modell wird für widersprüchlich erklärt, obwohl es lösbar ist.</li>
|
||
<li><strong>Ohne <code>num_workers = 1</code> ist der Lauf nicht reproduzierbar.</strong> Mehrere Suchstränge finden je nach Zeitverlauf verschiedene, gleich gute Lösungen. Für Tests und für abgedruckte Ausgaben Worker und Seed festnageln.</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="d5-cvxpy">D5 — CVXPY</h2>
|
||
<p><strong>Wofür.</strong> Konvexe Probleme: quadratische Ziele, Normen, CVaR, alles mit Regularisierungstermen. <strong>Wofür nicht:</strong> große kombinatorische Modelle — der Aufbau der Ausdrücke wird dann selbst zum Engpass.</p>
|
||
<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="im">import</span> numpy <span class="im">as</span> np</span>
|
||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> cvxpy <span class="im">as</span> cp</span>
|
||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>x <span class="op">=</span> cp.Variable(<span class="dv">3</span>, nonneg<span class="op">=</span><span class="va">True</span>) <span class="co"># nonneg=True statt x >= 0</span></span>
|
||
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>gewichte <span class="op">=</span> cp.Variable(<span class="dv">3</span>)</span>
|
||
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>ganzzahlig <span class="op">=</span> cp.Variable(<span class="dv">3</span>, integer<span class="op">=</span><span class="va">True</span>) <span class="co"># macht daraus ein MIP</span></span>
|
||
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>ziel <span class="op">=</span> cp.Maximize(np.array([<span class="fl">10.0</span>, <span class="fl">15.0</span>, <span class="fl">25.0</span>]) <span class="op">@</span> x)</span>
|
||
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>bedingungen <span class="op">=</span> [np.array([[<span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">2</span>], [<span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">1</span>]]) <span class="op">@</span> x <span class="op"><=</span> np.array([<span class="fl">40.0</span>, <span class="fl">50.0</span>])]</span>
|
||
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>problem <span class="op">=</span> cp.Problem(ziel, bedingungen)</span>
|
||
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="kw">not</span> problem.is_dcp(): <span class="co"># VOR dem Loesen pruefen</span></span>
|
||
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"nicht DCP:"</span>, [c <span class="cf">for</span> c <span class="kw">in</span> bedingungen <span class="cf">if</span> <span class="kw">not</span> c.is_dcp()])</span>
|
||
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>problem.solve()</span>
|
||
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> problem.status <span class="op">==</span> cp.OPTIMAL:</span>
|
||
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"optimal: </span><span class="sc">{</span>problem<span class="sc">.</span>value<span class="sc">:.2f}</span><span class="ss"> x = </span><span class="sc">{</span>np<span class="sc">.</span><span class="bu">round</span>(x.value, <span class="dv">4</span>)<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Schattenpreis: </span><span class="sc">{</span>np<span class="sc">.</span><span class="bu">round</span>(bedingungen[<span class="dv">0</span>].dual_value, <span class="dv">4</span>)<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> problem.status <span class="op">==</span> cp.OPTIMAL_INACCURATE:</span>
|
||
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Loesung numerisch unsicher - Skalierung pruefen, anderen Solver testen"</span>)</span>
|
||
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> problem.status <span class="op">==</span> cp.INFEASIBLE:</span>
|
||
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"INFEASIBLE"</span>)</span>
|
||
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> problem.status <span class="op">==</span> cp.UNBOUNDED:</span>
|
||
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"UNBOUNDED"</span>)</span>
|
||
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span>:</span>
|
||
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Solverfehler:"</span>, problem.status)</span></code></pre></div>
|
||
<h3 id="die-drei-häufigsten-stolpersteine-4">Die drei häufigsten Stolpersteine</h3>
|
||
<ol type="1">
|
||
<li><strong>DCP-Regeln vor dem Lösen prüfen.</strong> <code>problem.is_dcp()</code> und dann die einzelnen Bedingungen — das nennt die Schuldige, statt einen <code>DCPError</code> ohne Ort zu werfen (<a href="anhang-fehlerdiagnose.html#anhang-fehlerdiagnose">Anhang C</a>, C8).</li>
|
||
<li><strong><code>OPTIMAL_INACCURATE</code> ist kein Erfolg.</strong> Der Solver hat aufgegeben und meldet das leise. Diesen Fall immer eigens behandeln.</li>
|
||
<li><strong>Der Aufbau kann teurer sein als das Lösen.</strong> Schleifen über Szenarien durch Vektorausdrücke ersetzen; <code>Parameter</code> statt Neuaufbau, wenn sich nur Zahlen ändern.</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="d6-modellierungssprachen-pyomo-und-linopy">D6 — Modellierungssprachen: Pyomo und Linopy</h2>
|
||
<p><strong>Wofür.</strong> Beide trennen <em>Modell</em> von <em>Solver</em>: dasselbe Modell läuft ohne Änderung unter HiGHS, CBC, Gurobi. Pyomo denkt in <strong>Mengen und Indizes</strong> wie eine mathematische Formulierung; Linopy denkt in <strong>beschrifteten Arrays</strong> und baut Nebenbedingungen als Matrixoperation statt in Python-Schleifen. <strong>Wofür nicht:</strong> ein Modell mit zehn Nebenbedingungen — dort ist der Aufwand höher als der Nutzen.</p>
|
||
<h3 id="pyomo">Pyomo</h3>
|
||
<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> pyomo.environ <span class="im">as</span> pyo</span>
|
||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>modell <span class="op">=</span> pyo.ConcreteModel()</span>
|
||
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>modell.J <span class="op">=</span> pyo.RangeSet(<span class="dv">0</span>, <span class="dv">2</span>) <span class="co"># Indexmenge</span></span>
|
||
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>modell.x <span class="op">=</span> pyo.Var(modell.J, domain<span class="op">=</span>pyo.NonNegativeReals)</span>
|
||
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="co"># ganzzahlig: domain=pyo.NonNegativeIntegers | binaer: domain=pyo.Binary</span></span>
|
||
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>ertrag <span class="op">=</span> {<span class="dv">0</span>: <span class="fl">10.0</span>, <span class="dv">1</span>: <span class="fl">15.0</span>, <span class="dv">2</span>: <span class="fl">25.0</span>}</span>
|
||
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>modell.ziel <span class="op">=</span> pyo.Objective(expr<span class="op">=</span><span class="bu">sum</span>(ertrag[j] <span class="op">*</span> modell.x[j] <span class="cf">for</span> j <span class="kw">in</span> modell.J),</span>
|
||
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> sense<span class="op">=</span>pyo.maximize)</span>
|
||
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>modell.montage <span class="op">=</span> pyo.Constraint(</span>
|
||
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> expr<span class="op">=</span>modell.x[<span class="dv">0</span>] <span class="op">+</span> modell.x[<span class="dv">1</span>] <span class="op">+</span> <span class="dv">2</span> <span class="op">*</span> modell.x[<span class="dv">2</span>] <span class="op"><=</span> <span class="dv">40</span>)</span>
|
||
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>modell.pruefung <span class="op">=</span> pyo.Constraint(</span>
|
||
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> expr<span class="op">=</span><span class="dv">2</span> <span class="op">*</span> modell.x[<span class="dv">0</span>] <span class="op">+</span> <span class="dv">3</span> <span class="op">*</span> modell.x[<span class="dv">1</span>] <span class="op">+</span> modell.x[<span class="dv">2</span>] <span class="op"><=</span> <span class="dv">50</span>)</span>
|
||
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>modell.dual <span class="op">=</span> pyo.Suffix(direction<span class="op">=</span>pyo.Suffix.IMPORT) <span class="co"># fuer Schattenpreise</span></span>
|
||
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>ergebnis <span class="op">=</span> pyo.SolverFactory(<span class="st">"appsi_highs"</span>).solve(modell)</span>
|
||
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a>zustand <span class="op">=</span> ergebnis.solver.termination_condition</span>
|
||
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> zustand <span class="op">==</span> pyo.TerminationCondition.optimal:</span>
|
||
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"optimal: </span><span class="sc">{</span>pyo<span class="sc">.</span>value(modell.ziel)<span class="sc">:.2f}</span><span class="ss">"</span>)</span>
|
||
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"x = </span><span class="sc">{</span>[<span class="bu">round</span>(pyo.value(modell.x[j]), <span class="dv">4</span>) <span class="cf">for</span> j <span class="kw">in</span> modell.J]<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Schattenpreis Montage: </span><span class="sc">{</span>modell<span class="sc">.</span>dual[modell.montage]<span class="sc">:.4f}</span><span class="ss">"</span>)</span>
|
||
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> zustand <span class="op">==</span> pyo.TerminationCondition.infeasible:</span>
|
||
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"INFEASIBLE"</span>)</span>
|
||
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> zustand <span class="op">==</span> pyo.TerminationCondition.unbounded:</span>
|
||
<span id="cb7-26"><a href="#cb7-26" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"UNBOUNDED"</span>)</span>
|
||
<span id="cb7-27"><a href="#cb7-27" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> zustand <span class="op">==</span> pyo.TerminationCondition.maxTimeLimit:</span>
|
||
<span id="cb7-28"><a href="#cb7-28" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Zeitlimit"</span>)</span>
|
||
<span id="cb7-29"><a href="#cb7-29" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span>:</span>
|
||
<span id="cb7-30"><a href="#cb7-30" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"kein Optimum:"</span>, zustand)</span></code></pre></div>
|
||
<h3 id="linopy">Linopy</h3>
|
||
<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="im">import</span> linopy</span>
|
||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
|
||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> pandas <span class="im">as</span> pd</span>
|
||
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> xarray <span class="im">as</span> xr</span>
|
||
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Benannte Indizes statt blosser Listen - sonst heissen die Achsen "dim_0"</span></span>
|
||
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>produkt <span class="op">=</span> pd.Index([<span class="st">"Rahmen"</span>, <span class="st">"Gehaeuse"</span>, <span class="st">"Deckel"</span>], name<span class="op">=</span><span class="st">"produkt"</span>)</span>
|
||
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>ressource <span class="op">=</span> pd.Index([<span class="st">"Montage"</span>, <span class="st">"Pruefung"</span>], name<span class="op">=</span><span class="st">"ressource"</span>)</span>
|
||
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>modell <span class="op">=</span> linopy.Model()</span>
|
||
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>modell.add_variables(lower<span class="op">=</span><span class="dv">0</span>, coords<span class="op">=</span>[produkt], name<span class="op">=</span><span class="st">"menge"</span>)</span>
|
||
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>x <span class="op">=</span> modell.variables[<span class="st">"menge"</span>]</span>
|
||
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a><span class="co"># ganzzahlig: integer=True | binaer: binary=True</span></span>
|
||
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a>ertrag <span class="op">=</span> xr.DataArray([<span class="fl">10.0</span>, <span class="fl">15.0</span>, <span class="fl">25.0</span>], coords<span class="op">=</span>[produkt])</span>
|
||
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a>verbrauch <span class="op">=</span> xr.DataArray([[<span class="fl">1.0</span>, <span class="fl">1.0</span>, <span class="fl">2.0</span>], [<span class="fl">2.0</span>, <span class="fl">3.0</span>, <span class="fl">1.0</span>]],</span>
|
||
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a> coords<span class="op">=</span>[ressource, produkt])</span>
|
||
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a>vorrat <span class="op">=</span> xr.DataArray([<span class="fl">40.0</span>, <span class="fl">50.0</span>], coords<span class="op">=</span>[ressource])</span>
|
||
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a>modell.add_objective((ertrag <span class="op">*</span> x).<span class="bu">sum</span>(), sense<span class="op">=</span><span class="st">"max"</span>)</span>
|
||
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a><span class="co"># EINE Zeile, so viele Nebenbedingungen wie 'ressource' Eintraege hat:</span></span>
|
||
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true" tabindex="-1"></a>modell.add_constraints((verbrauch <span class="op">*</span> x).<span class="bu">sum</span>(<span class="st">"produkt"</span>) <span class="op"><=</span> vorrat, name<span class="op">=</span><span class="st">"kapazitaet"</span>)</span>
|
||
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true" tabindex="-1"></a>modell.solve(solver_name<span class="op">=</span><span class="st">"highs"</span>, output_flag<span class="op">=</span><span class="va">False</span>)</span>
|
||
<span id="cb8-25"><a href="#cb8-25" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb8-26"><a href="#cb8-26" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> modell.termination_condition <span class="op">==</span> <span class="st">"optimal"</span>:</span>
|
||
<span id="cb8-27"><a href="#cb8-27" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"optimal: </span><span class="sc">{</span>modell<span class="sc">.</span>objective<span class="sc">.</span>value<span class="sc">:.2f}</span><span class="ss">"</span>)</span>
|
||
<span id="cb8-28"><a href="#cb8-28" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(x.solution.to_series().<span class="bu">round</span>(<span class="dv">4</span>).to_dict())</span>
|
||
<span id="cb8-29"><a href="#cb8-29" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Schattenpreise:"</span>,</span>
|
||
<span id="cb8-30"><a href="#cb8-30" aria-hidden="true" tabindex="-1"></a> modell.constraints[<span class="st">"kapazitaet"</span>].dual.to_series().<span class="bu">round</span>(<span class="dv">4</span>).to_dict())</span>
|
||
<span id="cb8-31"><a href="#cb8-31" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> modell.termination_condition <span class="op">==</span> <span class="st">"infeasible"</span>:</span>
|
||
<span id="cb8-32"><a href="#cb8-32" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"INFEASIBLE"</span>)</span>
|
||
<span id="cb8-33"><a href="#cb8-33" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> modell.termination_condition <span class="op">==</span> <span class="st">"unbounded"</span>:</span>
|
||
<span id="cb8-34"><a href="#cb8-34" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"UNBOUNDED"</span>)</span>
|
||
<span id="cb8-35"><a href="#cb8-35" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span>:</span>
|
||
<span id="cb8-36"><a href="#cb8-36" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"kein Optimum:"</span>, modell.status, modell.termination_condition)</span></code></pre></div>
|
||
<h3 id="die-drei-häufigsten-stolpersteine-5">Die drei häufigsten Stolpersteine</h3>
|
||
<ol type="1">
|
||
<li><strong>Der Solver ist ein eigenes Programm.</strong> <code>SolverFactory("appsi_highs")</code> scheitert, wenn HiGHS nicht auffindbar ist — die Fehlermeldung nennt dann das Modell, nicht die fehlende Installation.</li>
|
||
<li><strong>Schattenpreise kommen nur auf Anforderung.</strong> Bei Pyomo braucht es <code>Suffix(direction=IMPORT)</code> <em>vor</em> dem Lösen; wer ihn vergisst, bekommt einen <code>KeyError</code> statt einer Warnung.</li>
|
||
<li><strong>Bei Linopy die Indizes benennen</strong> (<code>pd.Index(..., name="produkt")</code>). Ohne Namen heißen die Achsen <code>dim_0</code>, und jede spätere Zuordnung wird zum Ratespiel.</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="die-gemeinsame-regel">Die gemeinsame Regel</h2>
|
||
<p>Alle sechs Seiten haben denselben längsten Abschnitt: die <strong>Statusauswertung</strong>. Das ist kein Zufall. Ein Solveraufruf hat nie zwei Ausgänge, sondern mindestens fünf — optimal, zulässig ohne Beweis, unlösbar, unbeschränkt, abgebrochen. Code, der nur <code>if erfolgreich:</code> prüft, verwechselt früher oder später „keine Lösung gefunden“ mit „es gibt keine Lösung“, und diese Verwechslung merkt niemand, bis sie teuer wird.</p>
|
||
|
||
</article>
|
||
<button type="button" class="fortschritt-knopf" data-kapitel="anhang-spickzettel.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-fehlerdiagnose.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Anhang C: Fehlerdiagnose-Handbuch</span></a><a class="prev-next-knopf prev-next-next" href="anhang-glossar-literatur.html"><span><small>Weiter</small>Anhang E: Glossar und Literatur</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>
|