Neues Skript erzeuge_stichwortregister_04.py:
- Sucht alle Glossarbegriffe (Anzeigename) in den Kapiteldateien
und markiert die erste Fundstelle je Datei mit {idx:Indexmarke}
- Termliste aus glossar_eintraege_04.py + vorhandenen {idx:...}-Markern
- Idempotent: erkennt vorhandene Marker und überspringt sie
- Überspringt Codeblöcke, Inline-Code und Math
- --check und --bericht Modi
- 605 Marker eingefügt (2 Durchläufe: 597 + 8)
Zusätzlich:
- titeltexte_04.py _saeubern(): entfernt {idx:...}-Marker vor
Titeltext-Vergleich (pruefe_titeltexte schlug sonst fehl)
420 lines
74 KiB
HTML
Generated
420 lines
74 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{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"><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.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 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.html"><span><small>Weiter</small>Anhang E: Glossar</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.html"><span><small>Weiter</small>Anhang E: Glossar</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>
|