Phase 8.2: Solver-Isolation ohne subprocess-Codestrings
Setzt den Isolationsteil von Paket 1 aus Verbesserungen_02.md um. Der Plan
nannte zwei Programme; beim Suchen kam ein drittes dazu, das dasselbe Muster
verwendete.
Ein_System_Vier_Ansaetze.py und Benchmark_Skalierung.py hielten ihre vier
Solvervarianten als Zeichenketten in einem Dictionary und gaben sie an
"python -c" weiter - bei Benchmark_Skalierung.py sogar mit
.format()-Platzhaltern fuer die Instanzgroesse. Aus jeder Variante ist jetzt
eine gewoehnliche Funktion mit lokalem Import geworden.
Solverwechsel_CPSAT_HiGHS.py rief sich selbst ueber sys.argv erneut auf;
auch das entfaellt.
Ausgefuehrt wird ueber einen ProcessPoolExecutor mit zwei Einstellungen, die
beide noetig sind: mp_context "spawn" (frischer Interpreter statt geerbtem
Speicher - unter Linux ist fork der Standard) und max_tasks_per_child=1 (ein
neuer Prozess je Aufgabe; ohne das verwendet der Pool seinen Arbeiter
wieder, und beim zweiten Solver ist der Konflikt zurueck). Nachgemessen:
vier Aufgaben, vier verschiedene PIDs.
Der zweite Punkt hat einen eigenen Warnkasten bekommen, weil der Fehler
leicht zu machen und schwer zu finden ist: Der Absturz kaeme nicht beim
ersten Solver, sondern beim zweiten - und saehe aus wie ein Problem des
zweiten.
Regel 4, dreifach geprueft. Ein_System_Vier_Ansaetze.py: identisch bis auf
die Zeitspalte, einschliesslich der Spannweite 2,41e-08, auf die sich der
Merksatz des Kapitels beruft. Benchmark_Skalierung.py: alle zwoelf
Zielwerte und alle drei Spannweiten bitgleich; Zeiten und Speicher haben
sich verschoben, beide sind im Abdruck seit jeher als hardwareabhaengig
gekennzeichnet. Solverwechsel_CPSAT_HiGHS.py: Ausgabe ohne Zeiten
unveraendert.
Bewusst subprocess bleibt Mutationstest.py: Dort wird pytest auf einer
mutierten Kopie in einem temporaeren Verzeichnis gestartet - ein externes
Werkzeug auf veraenderten Dateien, nicht die Isolation eines Imports.
Neu im Kapitel Oekosystem: ein Abschnitt "Wie die Isolation aussieht, wenn
sie tragen soll" - warum ein Codestring die schlechteste Umsetzung von
"eigener Prozess" ist. Anhang C nennt jetzt ebenfalls ProcessPoolExecutor.
Ein eigener Fehler, gefunden und abgesichert: Ich hatte dem neuen ### ein
{#sec:...}-Label gegeben. ABSCHNITT_RE erkennt nur "## " - das Label waere
nie registriert worden und jeder Verweis darauf ins Leere gelaufen, ohne
Warnung. Label entfernt, --check meldet den Fall jetzt. Gegengetestet.
Stand: 818 Querverweise, 76 Programme, 33 pytest-Tests, PDF 760 Seiten, 69
netzfreie Programme fehlerfrei.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
9c83716095
commit
862e92bc7b
29 changed files with 2401 additions and 1912 deletions
|
|
@ -421,6 +421,35 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg</code></pre>
|
|||
<p><strong>Abhilfe:</strong> Jeden Solver in einem <strong>eigenen Prozess</strong> ausführen — genau das tut das folgende Programm. Alternativ: getrennte virtuelle Umgebungen, oder auf <code>highspy</code> verzichten und HiGHS über <code>scipy.optimize.linprog</code> bzw. CVXPY ansprechen (dort ist es ohnehin als Backend verfügbar).</p>
|
||||
<p>Der Installationstest im Vorspann umgeht die Falle bereits: Er lädt <code>ortools</code> zuerst, prüft <code>highspy</code> und <code>cvxpy</code> in der Paketübersicht nur auf Anwesenheit (<code>importlib.util.find_spec</code>) und importiert CVXPY erst im Funktionstest.</p>
|
||||
</blockquote>
|
||||
<h3 id="wie-die-isolation-aussieht-wenn-sie-tragen-soll">Wie die Isolation aussieht, wenn sie tragen soll</h3>
|
||||
<p>„Eigener Prozess” ist schnell gesagt. Die naheliegende Umsetzung — ein Codeschnipsel als Zeichenkette an <code>python -c</code> übergeben — funktioniert und ist trotzdem die schlechteste: Der Schnipsel ist für Editor, Linter und Testwerkzeug unsichtbar, ein Tippfehler darin fällt erst zur Laufzeit auf, und übergeben lassen sich nur Zeichenketten.</p>
|
||||
<p>Tragfähig ist stattdessen: <strong>jeder Solver eine gewöhnliche Funktion mit lokalem Import</strong>, ausgeführt von einem <code>ProcessPoolExecutor</code> mit zwei Einstellungen, die zusammen die Garantie ergeben:</p>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width: 50%" />
|
||||
<col style="width: 50%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th>Einstellung</th>
|
||||
<th>Wozu</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td><code>mp_context=multiprocessing.get_context("spawn")</code></td>
|
||||
<td>Der Kindprozess startet mit einem <strong>frischen</strong> Interpreter, statt den Speicher des Elternprozesses zu erben. Unter Linux ist <code>fork</code> der Standard — und damit wäre alles, was hier schon importiert ist, auch dort importiert.</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td><code>max_tasks_per_child=1</code></td>
|
||||
<td>Jede Aufgabe bekommt einen <strong>neuen</strong> Prozess. Ohne das verwendet der Pool seinen Arbeiter wieder, und beim zweiten Solver ist der Konflikt zurück. Genau dieser Fehler ist leicht zu machen und schwer zu finden.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<blockquote>
|
||||
<p><strong>⚠️ <code>max_tasks_per_child=1</code> ist nicht optional</strong> Ein Pool ohne diese Angabe ist der <strong>Normalfall</strong> — er soll seine Arbeiter ja wiederverwenden. Wer die Isolation über einen Pool herstellt und das vergisst, hat einen Prozesswechsel programmiert, aber keine Isolation gewonnen: Die zweite Aufgabe landet im selben Interpreter wie die erste. Der Absturz kommt dann nicht beim ersten Solver, sondern beim zweiten — und sieht aus wie ein Problem des zweiten.</p>
|
||||
</blockquote>
|
||||
<p>Denselben Aufbau verwenden <code>Solverwechsel_CPSAT_HiGHS.py</code> (<a href="praxisfallen.html#kap-praxisfallen">Kapitel 22</a>) und <code>Benchmark_Skalierung.py</code> (<a href="testing.html#kap-testing">Kapitel 23</a>). Dort wandern zusätzlich <strong>Datenobjekte</strong> über die Prozessgrenze statt Zeichenketten — möglich, weil Domänenmodell und Lösungs-DTO keinen Solver kennen (<a href="praxisfallen.html#sec:praxisfallen-or-kern">Abschnitt 22.6</a>).</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="co">#!/usr/bin/env python3</span></span>
|
||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Ein_System_Vier_Ansaetze.py</span></span>
|
||||
|
|
@ -431,132 +460,157 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg</code></pre>
|
|||
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="co"> 2*x1 + 3*x2 + x3 <= 50</span></span>
|
||||
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a><span class="co"> x >= 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><span class="co">Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich am Ende.</span></span>
|
||||
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="co">WICHTIG: Jeder Solver läuft in einem EIGENEN Prozess, weil sich ortools und</span></span>
|
||||
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a><span class="co">highspy auf vielen Systemen nicht gemeinsam importieren lassen (beide bringen</span></span>
|
||||
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a><span class="co">eine eigene HiGHS-Kopie mit -> Symbolkonflikt).</span></span>
|
||||
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a><span class="co">"""</span></span>
|
||||
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a><span class="co">Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich</span></span>
|
||||
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a><span class="co">am Ende.</span></span>
|
||||
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a><span class="co">WICHTIG: Jeder Solver laeuft in einem EIGENEN Prozess, weil sich ortools und</span></span>
|
||||
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a><span class="co">highspy auf vielen Systemen nicht gemeinsam importieren lassen (beide bringen</span></span>
|
||||
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a><span class="co">eine eigene HiGHS-Kopie mit -> Symbolkonflikt).</span></span>
|
||||
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> json</span>
|
||||
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> subprocess</span>
|
||||
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
|
||||
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> textwrap</span>
|
||||
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
|
||||
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a>ERWARTET <span class="op">=</span> <span class="fl">530.0</span> <span class="co"># Ergebnis der Handrechnung zum Produktionsprogramm</span></span>
|
||||
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a><span class="co"># Jeder Eintrag ist ein eigenständiges Miniprogramm, das sein Ergebnis als</span></span>
|
||||
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a><span class="co"># JSON auf stdout ausgibt. So bleibt jeder Import in seinem eigenen Prozess.</span></span>
|
||||
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a>ANSAETZE: <span class="bu">dict</span>[<span class="bu">str</span>, <span class="bu">str</span>] <span class="op">=</span> {</span>
|
||||
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a> <span class="st">"scipy.optimize.linprog"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb6-31"><a href="#cb6-31" aria-hidden="true" tabindex="-1"></a><span class="st"> from scipy.optimize import linprog</span></span>
|
||||
<span id="cb6-32"><a href="#cb6-32" aria-hidden="true" tabindex="-1"></a><span class="st"> res = linprog(c=[-10.0, -15.0, -25.0], # linprog MINIMIERT -> negieren</span></span>
|
||||
<span id="cb6-33"><a href="#cb6-33" aria-hidden="true" tabindex="-1"></a><span class="st"> A_ub=[[1, 1, 2], [2, 3, 1]], b_ub=[40, 50],</span></span>
|
||||
<span id="cb6-34"><a href="#cb6-34" aria-hidden="true" tabindex="-1"></a><span class="st"> bounds=[(0, None)] * 3, method="highs")</span></span>
|
||||
<span id="cb6-35"><a href="#cb6-35" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (-res.fun, list(res.x))</span></span>
|
||||
<span id="cb6-36"><a href="#cb6-36" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a><span class="co">Die Isolation besorgt ein ProcessPoolExecutor. Drei Einstellungen ergeben</span></span>
|
||||
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a><span class="co">zusammen die Garantie:</span></span>
|
||||
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a><span class="co"> mp_context "spawn" Der Kindprozess startet mit einem FRISCHEN</span></span>
|
||||
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a><span class="co"> Interpreter, statt den Speicher des Elternprozesses</span></span>
|
||||
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a><span class="co"> zu erben. Was hier schon importiert ist, ist dort</span></span>
|
||||
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a><span class="co"> nicht importiert. Mit dem Standard "fork" auf Linux</span></span>
|
||||
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a><span class="co"> waere das nicht so.</span></span>
|
||||
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a><span class="co"> max_tasks_per_child=1 Jede Aufgabe bekommt einen NEUEN Prozess. Ohne das</span></span>
|
||||
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a><span class="co"> wuerde der Pool seinen Arbeiter wiederverwenden - und</span></span>
|
||||
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a><span class="co"> beim zweiten Solver waere der Konflikt zurueck.</span></span>
|
||||
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a><span class="co"> max_workers=1 Haelt die vier Laeufe nacheinander. Nicht aus</span></span>
|
||||
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a><span class="co"> Vorsicht, sondern damit die gemessenen Zeiten</span></span>
|
||||
<span id="cb6-31"><a href="#cb6-31" aria-hidden="true" tabindex="-1"></a><span class="co"> vergleichbar bleiben.</span></span>
|
||||
<span id="cb6-32"><a href="#cb6-32" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-33"><a href="#cb6-33" aria-hidden="true" tabindex="-1"></a><span class="co">Jeder Solver steht in einer eigenen Funktion mit LOKALEM Import. Das ist der</span></span>
|
||||
<span id="cb6-34"><a href="#cb6-34" aria-hidden="true" tabindex="-1"></a><span class="co">Unterschied zu einem Codestring, den man an 'python -c' uebergibt: Die</span></span>
|
||||
<span id="cb6-35"><a href="#cb6-35" aria-hidden="true" tabindex="-1"></a><span class="co">Funktion laesst sich einzeln aufrufen, testen und vom Editor pruefen - ein</span></span>
|
||||
<span id="cb6-36"><a href="#cb6-36" aria-hidden="true" tabindex="-1"></a><span class="co">String nicht.</span></span>
|
||||
<span id="cb6-37"><a href="#cb6-37" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-38"><a href="#cb6-38" aria-hidden="true" tabindex="-1"></a> <span class="st">"highspy (natives HiGHS)"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb6-39"><a href="#cb6-39" aria-hidden="true" tabindex="-1"></a><span class="st"> import numpy as np, highspy</span></span>
|
||||
<span id="cb6-40"><a href="#cb6-40" aria-hidden="true" tabindex="-1"></a><span class="st"> h = highspy.Highs()</span></span>
|
||||
<span id="cb6-41"><a href="#cb6-41" aria-hidden="true" tabindex="-1"></a><span class="st"> h.setOptionValue("output_flag", False)</span></span>
|
||||
<span id="cb6-42"><a href="#cb6-42" aria-hidden="true" tabindex="-1"></a><span class="st"> h.addVars(3, np.zeros(3), np.full(3, highspy.kHighsInf))</span></span>
|
||||
<span id="cb6-43"><a href="#cb6-43" aria-hidden="true" tabindex="-1"></a><span class="st"> h.changeObjectiveSense(highspy.ObjSense.kMaximize)</span></span>
|
||||
<span id="cb6-44"><a href="#cb6-44" aria-hidden="true" tabindex="-1"></a><span class="st"> for j, wert in enumerate([10.0, 15.0, 25.0]):</span></span>
|
||||
<span id="cb6-45"><a href="#cb6-45" aria-hidden="true" tabindex="-1"></a><span class="st"> h.changeColCost(j, wert)</span></span>
|
||||
<span id="cb6-46"><a href="#cb6-46" aria-hidden="true" tabindex="-1"></a><span class="st"> # CSR-Format: starts[i] = Beginn von Zeile i in indices/values</span></span>
|
||||
<span id="cb6-47"><a href="#cb6-47" aria-hidden="true" tabindex="-1"></a><span class="st"> h.addRows(2, np.full(2, -highspy.kHighsInf), np.array([40.0, 50.0]), 6,</span></span>
|
||||
<span id="cb6-48"><a href="#cb6-48" aria-hidden="true" tabindex="-1"></a><span class="st"> np.array([0, 3], dtype=np.int32),</span></span>
|
||||
<span id="cb6-49"><a href="#cb6-49" aria-hidden="true" tabindex="-1"></a><span class="st"> np.array([0, 1, 2, 0, 1, 2], dtype=np.int32),</span></span>
|
||||
<span id="cb6-50"><a href="#cb6-50" aria-hidden="true" tabindex="-1"></a><span class="st"> np.array([1.0, 1.0, 2.0, 2.0, 3.0, 1.0]))</span></span>
|
||||
<span id="cb6-51"><a href="#cb6-51" aria-hidden="true" tabindex="-1"></a><span class="st"> h.run()</span></span>
|
||||
<span id="cb6-52"><a href="#cb6-52" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (h.getInfo().objective_function_value,</span></span>
|
||||
<span id="cb6-53"><a href="#cb6-53" aria-hidden="true" tabindex="-1"></a><span class="st"> list(h.getSolution().col_value[:3]))</span></span>
|
||||
<span id="cb6-54"><a href="#cb6-54" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb6-55"><a href="#cb6-55" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-56"><a href="#cb6-56" aria-hidden="true" tabindex="-1"></a> <span class="st">"cvxpy"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb6-57"><a href="#cb6-57" aria-hidden="true" tabindex="-1"></a><span class="st"> import numpy as np, cvxpy as cp</span></span>
|
||||
<span id="cb6-58"><a href="#cb6-58" aria-hidden="true" tabindex="-1"></a><span class="st"> x = cp.Variable(3, nonneg=True)</span></span>
|
||||
<span id="cb6-59"><a href="#cb6-59" aria-hidden="true" tabindex="-1"></a><span class="st"> problem = cp.Problem(cp.Maximize(np.array([10.0, 15.0, 25.0]) @ x),</span></span>
|
||||
<span id="cb6-60"><a href="#cb6-60" aria-hidden="true" tabindex="-1"></a><span class="st"> [np.array([[1, 1, 2], [2, 3, 1]]) @ x <= np.array([40, 50])])</span></span>
|
||||
<span id="cb6-61"><a href="#cb6-61" aria-hidden="true" tabindex="-1"></a><span class="st"> problem.solve()</span></span>
|
||||
<span id="cb6-62"><a href="#cb6-62" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (float(problem.value), [float(v) for v in x.value])</span></span>
|
||||
<span id="cb6-63"><a href="#cb6-63" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb6-64"><a href="#cb6-64" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-65"><a href="#cb6-65" aria-hidden="true" tabindex="-1"></a> <span class="st">"ortools / GLOP"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb6-66"><a href="#cb6-66" aria-hidden="true" tabindex="-1"></a><span class="st"> from ortools.linear_solver import pywraplp</span></span>
|
||||
<span id="cb6-67"><a href="#cb6-67" aria-hidden="true" tabindex="-1"></a><span class="st"> s = pywraplp.Solver.CreateSolver("GLOP")</span></span>
|
||||
<span id="cb6-68"><a href="#cb6-68" aria-hidden="true" tabindex="-1"></a><span class="st"> x = [s.NumVar(0, s.infinity(), f"x{j+1}") for j in range(3)]</span></span>
|
||||
<span id="cb6-69"><a href="#cb6-69" aria-hidden="true" tabindex="-1"></a><span class="st"> A = [[1, 1, 2], [2, 3, 1]]</span></span>
|
||||
<span id="cb6-70"><a href="#cb6-70" aria-hidden="true" tabindex="-1"></a><span class="st"> for i, kap in enumerate([40, 50]):</span></span>
|
||||
<span id="cb6-71"><a href="#cb6-71" aria-hidden="true" tabindex="-1"></a><span class="st"> s.Add(sum(A[i][j] * x[j] for j in range(3)) <= kap)</span></span>
|
||||
<span id="cb6-72"><a href="#cb6-72" aria-hidden="true" tabindex="-1"></a><span class="st"> s.Maximize(10 * x[0] + 15 * x[1] + 25 * x[2])</span></span>
|
||||
<span id="cb6-73"><a href="#cb6-73" aria-hidden="true" tabindex="-1"></a><span class="st"> s.Solve()</span></span>
|
||||
<span id="cb6-74"><a href="#cb6-74" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (s.Objective().Value(), [v.solution_value() for v in x])</span></span>
|
||||
<span id="cb6-75"><a href="#cb6-75" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb6-76"><a href="#cb6-76" aria-hidden="true" tabindex="-1"></a>}</span>
|
||||
<span id="cb6-77"><a href="#cb6-77" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-38"><a href="#cb6-38" aria-hidden="true" tabindex="-1"></a><span class="co">Benoetigt: scipy, highspy, cvxpy, ortools</span></span>
|
||||
<span id="cb6-39"><a href="#cb6-39" aria-hidden="true" tabindex="-1"></a><span class="co">"""</span></span>
|
||||
<span id="cb6-40"><a href="#cb6-40" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-41"><a href="#cb6-41" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> multiprocessing</span>
|
||||
<span id="cb6-42"><a href="#cb6-42" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
|
||||
<span id="cb6-43"><a href="#cb6-43" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> concurrent.futures <span class="im">import</span> ProcessPoolExecutor</span>
|
||||
<span id="cb6-44"><a href="#cb6-44" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-45"><a href="#cb6-45" aria-hidden="true" tabindex="-1"></a>ERWARTET <span class="op">=</span> <span class="fl">530.0</span> <span class="co"># Ergebnis der Handrechnung zum Produktionsprogramm</span></span>
|
||||
<span id="cb6-46"><a href="#cb6-46" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-47"><a href="#cb6-47" aria-hidden="true" tabindex="-1"></a><span class="co"># Die Instanz - einmal notiert, von allen vier Funktionen benutzt.</span></span>
|
||||
<span id="cb6-48"><a href="#cb6-48" aria-hidden="true" tabindex="-1"></a>ZIEL <span class="op">=</span> [<span class="fl">10.0</span>, <span class="fl">15.0</span>, <span class="fl">25.0</span>]</span>
|
||||
<span id="cb6-49"><a href="#cb6-49" aria-hidden="true" tabindex="-1"></a>MATRIX <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>]]</span>
|
||||
<span id="cb6-50"><a href="#cb6-50" aria-hidden="true" tabindex="-1"></a>KAPAZITAET <span class="op">=</span> [<span class="fl">40.0</span>, <span class="fl">50.0</span>]</span>
|
||||
<span id="cb6-51"><a href="#cb6-51" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-52"><a href="#cb6-52" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-53"><a href="#cb6-53" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> loese_mit_scipy() <span class="op">-></span> <span class="bu">tuple</span>[<span class="bu">float</span>, <span class="bu">list</span>[<span class="bu">float</span>]]:</span>
|
||||
<span id="cb6-54"><a href="#cb6-54" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> scipy.optimize <span class="im">import</span> linprog</span>
|
||||
<span id="cb6-55"><a href="#cb6-55" aria-hidden="true" tabindex="-1"></a> ergebnis <span class="op">=</span> linprog(c<span class="op">=</span>[<span class="op">-</span>w <span class="cf">for</span> w <span class="kw">in</span> ZIEL], <span class="co"># linprog MINIMIERT -> negieren</span></span>
|
||||
<span id="cb6-56"><a href="#cb6-56" aria-hidden="true" tabindex="-1"></a> A_ub<span class="op">=</span>MATRIX, b_ub<span class="op">=</span>KAPAZITAET,</span>
|
||||
<span id="cb6-57"><a href="#cb6-57" 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>, method<span class="op">=</span><span class="st">"highs"</span>)</span>
|
||||
<span id="cb6-58"><a href="#cb6-58" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="op">-</span>ergebnis.fun, <span class="bu">list</span>(ergebnis.x)</span>
|
||||
<span id="cb6-59"><a href="#cb6-59" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-60"><a href="#cb6-60" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-61"><a href="#cb6-61" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> loese_mit_highspy() <span class="op">-></span> <span class="bu">tuple</span>[<span class="bu">float</span>, <span class="bu">list</span>[<span class="bu">float</span>]]:</span>
|
||||
<span id="cb6-62"><a href="#cb6-62" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> highspy</span>
|
||||
<span id="cb6-63"><a href="#cb6-63" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> numpy <span class="im">as</span> np</span>
|
||||
<span id="cb6-64"><a href="#cb6-64" aria-hidden="true" tabindex="-1"></a> h <span class="op">=</span> highspy.Highs()</span>
|
||||
<span id="cb6-65"><a href="#cb6-65" aria-hidden="true" tabindex="-1"></a> h.setOptionValue(<span class="st">"output_flag"</span>, <span class="va">False</span>)</span>
|
||||
<span id="cb6-66"><a href="#cb6-66" 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="cb6-67"><a href="#cb6-67" aria-hidden="true" tabindex="-1"></a> h.changeObjectiveSense(highspy.ObjSense.kMaximize)</span>
|
||||
<span id="cb6-68"><a href="#cb6-68" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j, wert <span class="kw">in</span> <span class="bu">enumerate</span>(ZIEL):</span>
|
||||
<span id="cb6-69"><a href="#cb6-69" aria-hidden="true" tabindex="-1"></a> h.changeColCost(j, wert)</span>
|
||||
<span id="cb6-70"><a href="#cb6-70" aria-hidden="true" tabindex="-1"></a> <span class="co"># CSR-Format: starts[i] = Beginn von Zeile i in indices/values</span></span>
|
||||
<span id="cb6-71"><a href="#cb6-71" 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(KAPAZITAET), <span class="dv">6</span>,</span>
|
||||
<span id="cb6-72"><a href="#cb6-72" 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>
|
||||
<span id="cb6-73"><a href="#cb6-73" 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>
|
||||
<span id="cb6-74"><a href="#cb6-74" aria-hidden="true" tabindex="-1"></a> np.array([<span class="bu">float</span>(w) <span class="cf">for</span> zeile <span class="kw">in</span> MATRIX <span class="cf">for</span> w <span class="kw">in</span> zeile]))</span>
|
||||
<span id="cb6-75"><a href="#cb6-75" aria-hidden="true" tabindex="-1"></a> h.run()</span>
|
||||
<span id="cb6-76"><a href="#cb6-76" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> (h.getInfo().objective_function_value,</span>
|
||||
<span id="cb6-77"><a href="#cb6-77" aria-hidden="true" tabindex="-1"></a> <span class="bu">list</span>(h.getSolution().col_value[:<span class="dv">3</span>]))</span>
|
||||
<span id="cb6-78"><a href="#cb6-78" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-79"><a href="#cb6-79" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> fuehre_in_eigenem_prozess_aus(quelltext: <span class="bu">str</span>) <span class="op">-></span> <span class="bu">tuple</span>[<span class="bu">float</span>, <span class="bu">list</span>[<span class="bu">float</span>]]:</span>
|
||||
<span id="cb6-80"><a href="#cb6-80" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Startet den Codeschnipsel als separaten Python-Prozess und liest das Ergebnis."""</span></span>
|
||||
<span id="cb6-81"><a href="#cb6-81" aria-hidden="true" tabindex="-1"></a> programm <span class="op">=</span> textwrap.dedent(quelltext) <span class="op">+</span> <span class="st">"</span><span class="ch">\n</span><span class="st">import json; print(json.dumps(ausgabe))</span><span class="ch">\n</span><span class="st">"</span></span>
|
||||
<span id="cb6-82"><a href="#cb6-82" aria-hidden="true" tabindex="-1"></a> ergebnis <span class="op">=</span> subprocess.run([sys.executable, <span class="st">"-c"</span>, programm],</span>
|
||||
<span id="cb6-83"><a href="#cb6-83" aria-hidden="true" tabindex="-1"></a> capture_output<span class="op">=</span><span class="va">True</span>, text<span class="op">=</span><span class="va">True</span>, timeout<span class="op">=</span><span class="dv">120</span>)</span>
|
||||
<span id="cb6-84"><a href="#cb6-84" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> ergebnis.returncode <span class="op">!=</span> <span class="dv">0</span>:</span>
|
||||
<span id="cb6-85"><a href="#cb6-85" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">RuntimeError</span>(ergebnis.stderr.strip().splitlines()[<span class="op">-</span><span class="dv">1</span>])</span>
|
||||
<span id="cb6-86"><a href="#cb6-86" aria-hidden="true" tabindex="-1"></a> wert, loesung <span class="op">=</span> json.loads(ergebnis.stdout.strip().splitlines()[<span class="op">-</span><span class="dv">1</span>])</span>
|
||||
<span id="cb6-87"><a href="#cb6-87" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> wert, loesung</span>
|
||||
<span id="cb6-79"><a href="#cb6-79" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-80"><a href="#cb6-80" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> loese_mit_cvxpy() <span class="op">-></span> <span class="bu">tuple</span>[<span class="bu">float</span>, <span class="bu">list</span>[<span class="bu">float</span>]]:</span>
|
||||
<span id="cb6-81"><a href="#cb6-81" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> cvxpy <span class="im">as</span> cp</span>
|
||||
<span id="cb6-82"><a href="#cb6-82" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> numpy <span class="im">as</span> np</span>
|
||||
<span id="cb6-83"><a href="#cb6-83" 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>
|
||||
<span id="cb6-84"><a href="#cb6-84" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> cp.Problem(cp.Maximize(np.array(ZIEL) <span class="op">@</span> x),</span>
|
||||
<span id="cb6-85"><a href="#cb6-85" aria-hidden="true" tabindex="-1"></a> [np.array(MATRIX) <span class="op">@</span> x <span class="op"><=</span> np.array(KAPAZITAET)])</span>
|
||||
<span id="cb6-86"><a href="#cb6-86" aria-hidden="true" tabindex="-1"></a> problem.solve()</span>
|
||||
<span id="cb6-87"><a href="#cb6-87" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">float</span>(problem.value), [<span class="bu">float</span>(v) <span class="cf">for</span> v <span class="kw">in</span> x.value]</span>
|
||||
<span id="cb6-88"><a href="#cb6-88" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-89"><a href="#cb6-89" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-90"><a href="#cb6-90" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">"__main__"</span>:</span>
|
||||
<span id="cb6-91"><a href="#cb6-91" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">78</span>)</span>
|
||||
<span id="cb6-92"><a href="#cb6-92" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" EIN SYSTEM - VIER ANSAETZE (je eigener Prozess)"</span>)</span>
|
||||
<span id="cb6-93"><a href="#cb6-93" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">78</span>)</span>
|
||||
<span id="cb6-94"><a href="#cb6-94" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span><span class="st">'Bibliothek'</span><span class="sc">:<26}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Z*'</span><span class="sc">:>10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'x1'</span><span class="sc">:>7}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'x2'</span><span class="sc">:>7}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'x3'</span><span class="sc">:>7}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Zeit'</span><span class="sc">:>10}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb6-95"><a href="#cb6-95" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"-"</span> <span class="op">*</span> <span class="dv">78</span>)</span>
|
||||
<span id="cb6-96"><a href="#cb6-96" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-97"><a href="#cb6-97" aria-hidden="true" tabindex="-1"></a> werte <span class="op">=</span> []</span>
|
||||
<span id="cb6-98"><a href="#cb6-98" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, quelltext <span class="kw">in</span> ANSAETZE.items():</span>
|
||||
<span id="cb6-99"><a href="#cb6-99" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb6-100"><a href="#cb6-100" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span>:</span>
|
||||
<span id="cb6-101"><a href="#cb6-101" aria-hidden="true" tabindex="-1"></a> wert, x <span class="op">=</span> fuehre_in_eigenem_prozess_aus(quelltext)</span>
|
||||
<span id="cb6-102"><a href="#cb6-102" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> <span class="pp">RuntimeError</span> <span class="im">as</span> fehler:</span>
|
||||
<span id="cb6-103"><a href="#cb6-103" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span>name<span class="sc">:<26}</span><span class="ss"> nicht verfuegbar: </span><span class="sc">{</span>fehler[:<span class="dv">40</span>]<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb6-104"><a href="#cb6-104" aria-hidden="true" tabindex="-1"></a> <span class="cf">continue</span></span>
|
||||
<span id="cb6-105"><a href="#cb6-105" aria-hidden="true" tabindex="-1"></a> dauer <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb6-106"><a href="#cb6-106" aria-hidden="true" tabindex="-1"></a> werte.append(wert)</span>
|
||||
<span id="cb6-107"><a href="#cb6-107" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span>name<span class="sc">:<26}</span><span class="ss"> </span><span class="sc">{</span>wert<span class="sc">:>10.2f}</span><span class="ss"> </span><span class="sc">{</span>x[<span class="dv">0</span>]<span class="sc">:>7.2f}</span><span class="ss"> </span><span class="sc">{</span>x[<span class="dv">1</span>]<span class="sc">:>7.2f}</span><span class="ss"> </span><span class="sc">{</span>x[<span class="dv">2</span>]<span class="sc">:>7.2f}</span><span class="ss"> "</span></span>
|
||||
<span id="cb6-108"><a href="#cb6-108" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>dauer<span class="sc">:>8.2f}</span><span class="ss"> s"</span>)</span>
|
||||
<span id="cb6-109"><a href="#cb6-109" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-110"><a href="#cb6-110" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"-"</span> <span class="op">*</span> <span class="dv">78</span>)</span>
|
||||
<span id="cb6-111"><a href="#cb6-111" aria-hidden="true" tabindex="-1"></a> spanne <span class="op">=</span> <span class="bu">max</span>(werte) <span class="op">-</span> <span class="bu">min</span>(werte)</span>
|
||||
<span id="cb6-112"><a href="#cb6-112" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Spannweite zwischen den Bibliotheken: </span><span class="sc">{</span>spanne<span class="sc">:.2e}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb6-113"><a href="#cb6-113" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Abweichung zur Handrechnung (</span><span class="sc">{</span>ERWARTET<span class="sc">:.0f}</span><span class="ss">): "</span></span>
|
||||
<span id="cb6-114"><a href="#cb6-114" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="bu">abs</span>(werte[<span class="dv">0</span>] <span class="op">-</span> ERWARTET)<span class="sc">:.2e}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb6-115"><a href="#cb6-115" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> spanne <span class="op"><</span> <span class="fl">1e-6</span>, <span class="st">"Die Bibliotheken widersprechen sich!"</span></span>
|
||||
<span id="cb6-116"><a href="#cb6-116" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> <span class="bu">abs</span>(werte[<span class="dv">0</span>] <span class="op">-</span> ERWARTET) <span class="op"><</span> <span class="fl">1e-6</span>, <span class="st">"Ergebnis weicht von der Handrechnung ab!"</span></span>
|
||||
<span id="cb6-117"><a href="#cb6-117" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Alle Wege fuehren zum selben, von Hand bestaetigten Optimum."</span>)</span>
|
||||
<span id="cb6-118"><a href="#cb6-118" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"(Die Zeiten enthalten den Prozessstart und den Import - sie messen"</span>)</span>
|
||||
<span id="cb6-119"><a href="#cb6-119" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" NICHT die reine Solverleistung, siehe Uebung 3.5.)"</span>)</span>
|
||||
<span id="cb6-120"><a href="#cb6-120" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">78</span>)</span></code></pre></div>
|
||||
<span id="cb6-90"><a href="#cb6-90" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> loese_mit_ortools() <span class="op">-></span> <span class="bu">tuple</span>[<span class="bu">float</span>, <span class="bu">list</span>[<span class="bu">float</span>]]:</span>
|
||||
<span id="cb6-91"><a href="#cb6-91" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> ortools.linear_solver <span class="im">import</span> pywraplp</span>
|
||||
<span id="cb6-92"><a href="#cb6-92" aria-hidden="true" tabindex="-1"></a> s <span class="op">=</span> pywraplp.Solver.CreateSolver(<span class="st">"GLOP"</span>)</span>
|
||||
<span id="cb6-93"><a href="#cb6-93" aria-hidden="true" tabindex="-1"></a> x <span class="op">=</span> [s.NumVar(<span class="dv">0</span>, s.infinity(), <span class="ss">f"x</span><span class="sc">{</span>j<span class="op">+</span><span class="dv">1</span><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="cb6-94"><a href="#cb6-94" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i, kapazitaet <span class="kw">in</span> <span class="bu">enumerate</span>(KAPAZITAET):</span>
|
||||
<span id="cb6-95"><a href="#cb6-95" aria-hidden="true" tabindex="-1"></a> s.Add(<span class="bu">sum</span>(MATRIX[i][j] <span class="op">*</span> x[j] <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">3</span>)) <span class="op"><=</span> kapazitaet)</span>
|
||||
<span id="cb6-96"><a href="#cb6-96" aria-hidden="true" tabindex="-1"></a> s.Maximize(<span class="bu">sum</span>(ZIEL[j] <span class="op">*</span> x[j] <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">3</span>)))</span>
|
||||
<span id="cb6-97"><a href="#cb6-97" aria-hidden="true" tabindex="-1"></a> s.Solve()</span>
|
||||
<span id="cb6-98"><a href="#cb6-98" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> s.Objective().Value(), [v.solution_value() <span class="cf">for</span> v <span class="kw">in</span> x]</span>
|
||||
<span id="cb6-99"><a href="#cb6-99" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-100"><a href="#cb6-100" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-101"><a href="#cb6-101" aria-hidden="true" tabindex="-1"></a>ANSAETZE <span class="op">=</span> {</span>
|
||||
<span id="cb6-102"><a href="#cb6-102" aria-hidden="true" tabindex="-1"></a> <span class="st">"scipy.optimize.linprog"</span>: loese_mit_scipy,</span>
|
||||
<span id="cb6-103"><a href="#cb6-103" aria-hidden="true" tabindex="-1"></a> <span class="st">"highspy (natives HiGHS)"</span>: loese_mit_highspy,</span>
|
||||
<span id="cb6-104"><a href="#cb6-104" aria-hidden="true" tabindex="-1"></a> <span class="st">"cvxpy"</span>: loese_mit_cvxpy,</span>
|
||||
<span id="cb6-105"><a href="#cb6-105" aria-hidden="true" tabindex="-1"></a> <span class="st">"ortools / GLOP"</span>: loese_mit_ortools,</span>
|
||||
<span id="cb6-106"><a href="#cb6-106" aria-hidden="true" tabindex="-1"></a>}</span>
|
||||
<span id="cb6-107"><a href="#cb6-107" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-108"><a href="#cb6-108" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-109"><a href="#cb6-109" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">"__main__"</span>:</span>
|
||||
<span id="cb6-110"><a href="#cb6-110" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">78</span>)</span>
|
||||
<span id="cb6-111"><a href="#cb6-111" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" EIN SYSTEM - VIER ANSAETZE (je eigener Prozess)"</span>)</span>
|
||||
<span id="cb6-112"><a href="#cb6-112" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">78</span>)</span>
|
||||
<span id="cb6-113"><a href="#cb6-113" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span><span class="st">'Bibliothek'</span><span class="sc">:<26}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Z*'</span><span class="sc">:>10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'x1'</span><span class="sc">:>7}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'x2'</span><span class="sc">:>7}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'x3'</span><span class="sc">:>7}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Zeit'</span><span class="sc">:>10}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb6-114"><a href="#cb6-114" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"-"</span> <span class="op">*</span> <span class="dv">78</span>)</span>
|
||||
<span id="cb6-115"><a href="#cb6-115" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-116"><a href="#cb6-116" aria-hidden="true" tabindex="-1"></a> werte <span class="op">=</span> []</span>
|
||||
<span id="cb6-117"><a href="#cb6-117" aria-hidden="true" tabindex="-1"></a> <span class="co"># Ein Pool, vier Aufgaben, vier frische Prozesse. Der Kontext muss</span></span>
|
||||
<span id="cb6-118"><a href="#cb6-118" aria-hidden="true" tabindex="-1"></a> <span class="co"># "spawn" sein - siehe Modulkommentar.</span></span>
|
||||
<span id="cb6-119"><a href="#cb6-119" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> ProcessPoolExecutor(</span>
|
||||
<span id="cb6-120"><a href="#cb6-120" aria-hidden="true" tabindex="-1"></a> max_workers<span class="op">=</span><span class="dv">1</span>,</span>
|
||||
<span id="cb6-121"><a href="#cb6-121" aria-hidden="true" tabindex="-1"></a> mp_context<span class="op">=</span>multiprocessing.get_context(<span class="st">"spawn"</span>),</span>
|
||||
<span id="cb6-122"><a href="#cb6-122" aria-hidden="true" tabindex="-1"></a> max_tasks_per_child<span class="op">=</span><span class="dv">1</span>) <span class="im">as</span> pool:</span>
|
||||
<span id="cb6-123"><a href="#cb6-123" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, funktion <span class="kw">in</span> ANSAETZE.items():</span>
|
||||
<span id="cb6-124"><a href="#cb6-124" aria-hidden="true" tabindex="-1"></a> beginn <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb6-125"><a href="#cb6-125" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span>:</span>
|
||||
<span id="cb6-126"><a href="#cb6-126" aria-hidden="true" tabindex="-1"></a> wert, x <span class="op">=</span> pool.submit(funktion).result(timeout<span class="op">=</span><span class="dv">120</span>)</span>
|
||||
<span id="cb6-127"><a href="#cb6-127" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> <span class="pp">Exception</span> <span class="im">as</span> fehler: <span class="co"># Bibliothek fehlt o. Ae.</span></span>
|
||||
<span id="cb6-128"><a href="#cb6-128" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span>name<span class="sc">:<26}</span><span class="ss"> nicht verfuegbar: </span><span class="sc">{</span><span class="bu">str</span>(fehler)[:<span class="dv">40</span>]<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb6-129"><a href="#cb6-129" aria-hidden="true" tabindex="-1"></a> <span class="cf">continue</span></span>
|
||||
<span id="cb6-130"><a href="#cb6-130" aria-hidden="true" tabindex="-1"></a> dauer <span class="op">=</span> time.perf_counter() <span class="op">-</span> beginn</span>
|
||||
<span id="cb6-131"><a href="#cb6-131" aria-hidden="true" tabindex="-1"></a> werte.append(wert)</span>
|
||||
<span id="cb6-132"><a href="#cb6-132" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span>name<span class="sc">:<26}</span><span class="ss"> </span><span class="sc">{</span>wert<span class="sc">:>10.2f}</span><span class="ss"> </span><span class="sc">{</span>x[<span class="dv">0</span>]<span class="sc">:>7.2f}</span><span class="ss"> </span><span class="sc">{</span>x[<span class="dv">1</span>]<span class="sc">:>7.2f}</span><span class="ss"> </span><span class="sc">{</span>x[<span class="dv">2</span>]<span class="sc">:>7.2f}</span><span class="ss"> "</span></span>
|
||||
<span id="cb6-133"><a href="#cb6-133" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>dauer<span class="sc">:>8.2f}</span><span class="ss"> s"</span>)</span>
|
||||
<span id="cb6-134"><a href="#cb6-134" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-135"><a href="#cb6-135" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"-"</span> <span class="op">*</span> <span class="dv">78</span>)</span>
|
||||
<span id="cb6-136"><a href="#cb6-136" aria-hidden="true" tabindex="-1"></a> spanne <span class="op">=</span> <span class="bu">max</span>(werte) <span class="op">-</span> <span class="bu">min</span>(werte)</span>
|
||||
<span id="cb6-137"><a href="#cb6-137" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Spannweite zwischen den Bibliotheken: </span><span class="sc">{</span>spanne<span class="sc">:.2e}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb6-138"><a href="#cb6-138" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Abweichung zur Handrechnung (</span><span class="sc">{</span>ERWARTET<span class="sc">:.0f}</span><span class="ss">): "</span></span>
|
||||
<span id="cb6-139"><a href="#cb6-139" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="bu">abs</span>(werte[<span class="dv">0</span>] <span class="op">-</span> ERWARTET)<span class="sc">:.2e}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb6-140"><a href="#cb6-140" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> spanne <span class="op"><</span> <span class="fl">1e-6</span>, <span class="st">"Die Bibliotheken widersprechen sich!"</span></span>
|
||||
<span id="cb6-141"><a href="#cb6-141" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> <span class="bu">abs</span>(werte[<span class="dv">0</span>] <span class="op">-</span> ERWARTET) <span class="op"><</span> <span class="fl">1e-6</span>, <span class="st">"Ergebnis weicht von der Handrechnung ab!"</span></span>
|
||||
<span id="cb6-142"><a href="#cb6-142" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Alle Wege fuehren zum selben, von Hand bestaetigten Optimum."</span>)</span>
|
||||
<span id="cb6-143"><a href="#cb6-143" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"(Die Zeiten enthalten Prozessstart und Import - sie messen NICHT die"</span>)</span>
|
||||
<span id="cb6-144"><a href="#cb6-144" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" reine Solverleistung. Die Uebungsaufgabe 'Laufzeitvergleich' trennt beides.)"</span>)</span>
|
||||
<span id="cb6-145"><a href="#cb6-145" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">78</span>)</span></code></pre></div>
|
||||
<p><strong>Erwartete Ausgabe (Zeiten hardwareabhängig):</strong></p>
|
||||
<pre><code>==============================================================================
|
||||
EIN SYSTEM - VIER ANSAETZE (je eigener Prozess)
|
||||
==============================================================================
|
||||
Bibliothek Z* x1 x2 x3 Zeit
|
||||
------------------------------------------------------------------------------
|
||||
scipy.optimize.linprog 530.00 0.00 12.00 14.00 0.55 s
|
||||
highspy (natives HiGHS) 530.00 0.00 12.00 14.00 0.17 s
|
||||
cvxpy 530.00 0.00 12.00 14.00 1.52 s
|
||||
ortools / GLOP 530.00 0.00 12.00 14.00 0.09 s
|
||||
scipy.optimize.linprog 530.00 0.00 12.00 14.00 0.59 s
|
||||
highspy (natives HiGHS) 530.00 0.00 12.00 14.00 0.12 s
|
||||
cvxpy 530.00 0.00 12.00 14.00 1.24 s
|
||||
ortools / GLOP 530.00 0.00 12.00 14.00 0.33 s
|
||||
------------------------------------------------------------------------------
|
||||
Spannweite zwischen den Bibliotheken: 2.41e-08
|
||||
Abweichung zur Handrechnung (530): 0.00e+00
|
||||
Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.
|
||||
(Die Zeiten enthalten den Prozessstart und den Import - sie messen
|
||||
NICHT die reine Solverleistung, siehe Uebung 3.5.)
|
||||
(Die Zeiten enthalten Prozessstart und Import - sie messen NICHT die
|
||||
reine Solverleistung. Die Uebungsaufgabe 'Laufzeitvergleich' trennt beides.)
|
||||
==============================================================================</code></pre>
|
||||
<blockquote>
|
||||
<p><strong>🎯 Merksatz zur Spannweite</strong> Die vier Bibliotheken stimmen <strong>nicht auf die letzte Stelle</strong> überein, sondern nur bis auf <span class="math inline">2{,}4 \times 10^{-8}</span>. Das ist normal: Solver arbeiten mit endlicher Genauigkeit und brechen ab, sobald ihre eigene Toleranz erreicht ist. <strong>Vergleichen Sie Solver-Ergebnisse deshalb nie mit <code>==</code></strong>, sondern immer mit einer Toleranz — <code>abs(a - b) < 1e-6</code> oder <code>np.isclose()</code>. Wer auf exakte Gleichheit prüft, baut sich Tests, die zufällig mal bestehen und mal nicht.</p>
|
||||
|
|
|
|||
Loading…
Reference in a new issue