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:
dschlueter 2026-09-08 12:39:34 +02:00
commit 862e92bc7b
29 changed files with 2401 additions and 1912 deletions

View file

@ -1790,9 +1790,9 @@ Domaenenschicht.
<span id="cb10-36"><a href="#cb10-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-37"><a href="#cb10-37" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
<span id="cb10-38"><a href="#cb10-38" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-39"><a href="#cb10-39" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> subprocess</span>
<span id="cb10-40"><a href="#cb10-40" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
<span id="cb10-41"><a href="#cb10-41" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
<span id="cb10-39"><a href="#cb10-39" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> multiprocessing</span>
<span id="cb10-40"><a href="#cb10-40" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
<span id="cb10-41"><a href="#cb10-41" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> concurrent.futures <span class="im">import</span> ProcessPoolExecutor</span>
<span id="cb10-42"><a href="#cb10-42" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-43"><a href="#cb10-43" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb10-44"><a href="#cb10-44" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> pydantic <span class="im">import</span> BaseModel, Field, model_validator</span>
@ -1995,83 +1995,87 @@ Domaenenschicht.
<span id="cb10-241"><a href="#cb10-241" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">any</span>(loesung.werte[problem.schluessel(i, j)] <span class="op">&gt;</span> <span class="fl">0.5</span> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(m))]</span>
<span id="cb10-242"><a href="#cb10-242" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-243"><a href="#cb10-243" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-244"><a href="#cb10-244" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> loese_in_eigenem_prozess(name: <span class="bu">str</span>) <span class="op">-&gt;</span> Loesung:</span>
<span id="cb10-245"><a href="#cb10-245" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Startet dieses Programm noch einmal - mit genau einem Solverimport.&quot;&quot;&quot;</span></span>
<span id="cb10-246"><a href="#cb10-246" aria-hidden="true" tabindex="-1"></a> ergebnis <span class="op">=</span> subprocess.run([sys.executable, <span class="va">__file__</span>, name],</span>
<span id="cb10-247"><a href="#cb10-247" 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">300</span>)</span>
<span id="cb10-248"><a href="#cb10-248" 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="cb10-249"><a href="#cb10-249" 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="cb10-250"><a href="#cb10-250" aria-hidden="true" tabindex="-1"></a> <span class="co"># Das DTO als JSON - genau dafuer ist ein Datenobjekt ohne Solverbezug gut.</span></span>
<span id="cb10-251"><a href="#cb10-251" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> Loesung.model_validate_json(ergebnis.stdout.strip().splitlines()[<span class="op">-</span><span class="dv">1</span>])</span>
<span id="cb10-244"><a href="#cb10-244" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> loese_in_eigenem_prozess(name: <span class="bu">str</span>, problem: Standortproblem) <span class="op">-&gt;</span> Loesung:</span>
<span id="cb10-245"><a href="#cb10-245" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Laesst genau einen Modellbauer in einem frischen Prozess rechnen.</span></span>
<span id="cb10-246"><a href="#cb10-246" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-247"><a href="#cb10-247" aria-hidden="true" tabindex="-1"></a><span class="co"> &#39;spawn&#39; statt des Linux-Standards &#39;fork&#39;: Der Kindprozess startet mit</span></span>
<span id="cb10-248"><a href="#cb10-248" aria-hidden="true" tabindex="-1"></a><span class="co"> einem leeren Interpreter und importiert nur den Solver, den SEIN</span></span>
<span id="cb10-249"><a href="#cb10-249" aria-hidden="true" tabindex="-1"></a><span class="co"> Modellbauer braucht. max_tasks_per_child=1 sorgt dafuer, dass der Pool</span></span>
<span id="cb10-250"><a href="#cb10-250" aria-hidden="true" tabindex="-1"></a><span class="co"> seinen Arbeiter nicht wiederverwendet - sonst saessen beim zweiten Aufruf</span></span>
<span id="cb10-251"><a href="#cb10-251" aria-hidden="true" tabindex="-1"></a><span class="co"> wieder beide Bibliotheken im selben Prozess.</span></span>
<span id="cb10-252"><a href="#cb10-252" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-253"><a href="#cb10-253" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-254"><a href="#cb10-254" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb10-255"><a href="#cb10-255" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> beispielproblem()</span>
<span id="cb10-256"><a href="#cb10-256" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-257"><a href="#cb10-257" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Kindprozess: rechnen und das DTO als JSON ausgeben ---------------</span></span>
<span id="cb10-258"><a href="#cb10-258" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">len</span>(sys.argv) <span class="op">&gt;</span> <span class="dv">1</span>:</span>
<span id="cb10-259"><a href="#cb10-259" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(MODELLBAUER[sys.argv[<span class="dv">1</span>]](problem).model_dump_json())</span>
<span id="cb10-260"><a href="#cb10-260" aria-hidden="true" tabindex="-1"></a> sys.exit(<span class="dv">0</span>)</span>
<span id="cb10-253"><a href="#cb10-253" aria-hidden="true" tabindex="-1"></a><span class="co"> Hin und zurueck wandert das Domaenenmodell bzw. das Loesungs-DTO. Beide</span></span>
<span id="cb10-254"><a href="#cb10-254" aria-hidden="true" tabindex="-1"></a><span class="co"> kennen keinen Solver, sind also serialisierbar - genau dafuer sind sie da.</span></span>
<span id="cb10-255"><a href="#cb10-255" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb10-256"><a href="#cb10-256" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> ProcessPoolExecutor(</span>
<span id="cb10-257"><a href="#cb10-257" aria-hidden="true" tabindex="-1"></a> max_workers<span class="op">=</span><span class="dv">1</span>,</span>
<span id="cb10-258"><a href="#cb10-258" aria-hidden="true" tabindex="-1"></a> mp_context<span class="op">=</span>multiprocessing.get_context(<span class="st">&quot;spawn&quot;</span>),</span>
<span id="cb10-259"><a href="#cb10-259" 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="cb10-260"><a href="#cb10-260" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> pool.submit(MODELLBAUER[name], problem).result(timeout<span class="op">=</span><span class="dv">300</span>)</span>
<span id="cb10-261"><a href="#cb10-261" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-262"><a href="#cb10-262" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Hauptprozess: beide Solver anstossen und vergleichen -------------</span></span>
<span id="cb10-263"><a href="#cb10-263" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb10-264"><a href="#cb10-264" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE&quot;</span>)</span>
<span id="cb10-265"><a href="#cb10-265" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb10-266"><a href="#cb10-266" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Standortplanung: </span><span class="sc">{</span><span class="bu">len</span>(problem.lager)<span class="sc">}</span><span class="ss"> moegliche Lager, &quot;</span></span>
<span id="cb10-267"><a href="#cb10-267" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="bu">len</span>(problem.kunden)<span class="sc">}</span><span class="ss"> Kunden, </span><span class="sc">{</span><span class="bu">sum</span>(problem.bedarf)<span class="sc">}</span><span class="ss"> Paletten Bedarf.&quot;</span>)</span>
<span id="cb10-268"><a href="#cb10-268" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Kapazitaet je Lager: </span><span class="sc">{</span>problem<span class="sc">.</span>kapazitaet[<span class="dv">0</span>]<span class="sc">}</span><span class="ss"> Paletten &quot;</span></span>
<span id="cb10-269"><a href="#cb10-269" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;-&gt; mindestens 3 Lager noetig.</span><span class="ch">\n</span><span class="ss">&quot;</span>)</span>
<span id="cb10-270"><a href="#cb10-270" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-271"><a href="#cb10-271" aria-hidden="true" tabindex="-1"></a> loesungen: <span class="bu">dict</span>[<span class="bu">str</span>, Loesung] <span class="op">=</span> {}</span>
<span id="cb10-272"><a href="#cb10-272" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, beschriftung <span class="kw">in</span> [(<span class="st">&quot;cpsat&quot;</span>, <span class="st">&quot;OR-Tools CP-SAT&quot;</span>),</span>
<span id="cb10-273"><a href="#cb10-273" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;highs&quot;</span>, <span class="st">&quot;HiGHS (highspy)&quot;</span>)]:</span>
<span id="cb10-274"><a href="#cb10-274" aria-hidden="true" tabindex="-1"></a> loesung <span class="op">=</span> loesungen[name] <span class="op">=</span> loese_in_eigenem_prozess(name)</span>
<span id="cb10-275"><a href="#cb10-275" aria-hidden="true" tabindex="-1"></a> beanstandungen <span class="op">=</span> pruefe_zuordnung(problem, loesung)</span>
<span id="cb10-276"><a href="#cb10-276" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-277"><a href="#cb10-277" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>beschriftung<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-278"><a href="#cb10-278" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span>loesung<span class="sc">.</span>als_bericht()<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-279"><a href="#cb10-279" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; eroeffnete Lager: </span><span class="sc">{</span><span class="st">&#39;, &#39;</span><span class="sc">.</span>join(geoeffnete_lager(problem, loesung))<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-280"><a href="#cb10-280" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Abnahmepruefung: &quot;</span></span>
<span id="cb10-281"><a href="#cb10-281" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;bestanden&#39;</span> <span class="cf">if</span> <span class="kw">not</span> beanstandungen <span class="cf">else</span> beanstandungen<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-282"><a href="#cb10-282" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-283"><a href="#cb10-283" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Was der Vergleich zeigt -----------------------------------------</span></span>
<span id="cb10-284"><a href="#cb10-284" aria-hidden="true" tabindex="-1"></a> zielwerte <span class="op">=</span> [loesung.zielwert <span class="cf">for</span> loesung <span class="kw">in</span> loesungen.values()]</span>
<span id="cb10-285"><a href="#cb10-285" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb10-286"><a href="#cb10-286" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Zielwertdifferenz: </span><span class="sc">{</span><span class="bu">abs</span>(zielwerte[<span class="dv">0</span>] <span class="op">-</span> zielwerte[<span class="dv">1</span>])<span class="sc">:.6f}</span><span class="ss"> EUR&quot;</span>)</span>
<span id="cb10-287"><a href="#cb10-287" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-288"><a href="#cb10-288" aria-hidden="true" tabindex="-1"></a> gleich_belegt <span class="op">=</span> <span class="bu">all</span>(</span>
<span id="cb10-289"><a href="#cb10-289" aria-hidden="true" tabindex="-1"></a> <span class="bu">round</span>(loesungen[<span class="st">&quot;cpsat&quot;</span>].werte[s]) <span class="op">==</span> <span class="bu">round</span>(loesungen[<span class="st">&quot;highs&quot;</span>].werte[s])</span>
<span id="cb10-290"><a href="#cb10-290" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> s <span class="kw">in</span> loesungen[<span class="st">&quot;cpsat&quot;</span>].werte)</span>
<span id="cb10-291"><a href="#cb10-291" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Identische Zuordnung: </span><span class="sc">{</span><span class="st">&#39;ja&#39;</span> <span class="cf">if</span> gleich_belegt <span class="cf">else</span> <span class="st">&#39;nein&#39;</span><span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-292"><a href="#cb10-292" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-293"><a href="#cb10-293" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> <span class="bu">abs</span>(zielwerte[<span class="dv">0</span>] <span class="op">-</span> zielwerte[<span class="dv">1</span>]) <span class="op">&lt;</span> <span class="fl">0.5</span>, <span class="st">&quot;Die Solver widersprechen sich!&quot;</span></span>
<span id="cb10-294"><a href="#cb10-294" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> <span class="bu">all</span>(l.status <span class="kw">is</span> SolverStatus.OPTIMAL <span class="cf">for</span> l <span class="kw">in</span> loesungen.values())</span>
<span id="cb10-295"><a href="#cb10-295" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-296"><a href="#cb10-296" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb10-297"><a href="#cb10-297" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; WAS DER WECHSEL GEKOSTET HAT&quot;</span>)</span>
<span id="cb10-298"><a href="#cb10-298" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb10-299"><a href="#cb10-299" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Ausgetauscht wurde EINE Funktion. Domaenenmodell, Abnahmepruefung und&quot;</span>)</span>
<span id="cb10-300"><a href="#cb10-300" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Bericht sind woertlich dieselben - sie sehen den Solver nie.&quot;</span>)</span>
<span id="cb10-301"><a href="#cb10-301" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
<span id="cb10-302"><a href="#cb10-302" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Nicht umsonst ist der Wechsel trotzdem:&quot;</span>)</span>
<span id="cb10-303"><a href="#cb10-303" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; * CP-SAT rechnet ausschliesslich GANZZAHLIG. Alle Kosten sind hier&quot;</span>)</span>
<span id="cb10-304"><a href="#cb10-304" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; deshalb int. Wer in Euro und Cent rechnet, skaliert vorher auf Cent -&quot;</span>)</span>
<span id="cb10-305"><a href="#cb10-305" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; und muss das im Bericht wieder zuruecknehmen.&quot;</span>)</span>
<span id="cb10-306"><a href="#cb10-306" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; * HiGHS braucht die Restriktionen als Matrixzeilen, CP-SAT nimmt sie&quot;</span>)</span>
<span id="cb10-307"><a href="#cb10-307" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; als Ausdruecke. Das ist der Grund, warum der HiGHS-Modellbauer&quot;</span>)</span>
<span id="cb10-308"><a href="#cb10-308" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; laenger ist, obwohl er dasselbe Modell beschreibt.&quot;</span>)</span>
<span id="cb10-309"><a href="#cb10-309" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; * Beide Bibliotheken bringen eine eigene HiGHS-Kopie mit und lassen&quot;</span>)</span>
<span id="cb10-310"><a href="#cb10-310" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; sich nicht gemeinsam importieren - daher die zwei Prozesse.&quot;</span>)</span>
<span id="cb10-311"><a href="#cb10-311" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
<span id="cb10-312"><a href="#cb10-312" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Der Ertrag: Beide beweisen denselben optimalen Zielwert, und die&quot;</span>)</span>
<span id="cb10-313"><a href="#cb10-313" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Entscheidung zwischen ihnen ist eine Frage der Laufzeit geworden -&quot;</span>)</span>
<span id="cb10-314"><a href="#cb10-314" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;nicht eine Frage, wie viel Code man neu schreiben muss.&quot;</span>)</span>
<span id="cb10-262"><a href="#cb10-262" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-263"><a href="#cb10-263" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb10-264"><a href="#cb10-264" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> beispielproblem()</span>
<span id="cb10-265"><a href="#cb10-265" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-266"><a href="#cb10-266" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Beide Solver anstossen und vergleichen ---------------------------</span></span>
<span id="cb10-267"><a href="#cb10-267" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb10-268"><a href="#cb10-268" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE&quot;</span>)</span>
<span id="cb10-269"><a href="#cb10-269" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb10-270"><a href="#cb10-270" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Standortplanung: </span><span class="sc">{</span><span class="bu">len</span>(problem.lager)<span class="sc">}</span><span class="ss"> moegliche Lager, &quot;</span></span>
<span id="cb10-271"><a href="#cb10-271" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="bu">len</span>(problem.kunden)<span class="sc">}</span><span class="ss"> Kunden, </span><span class="sc">{</span><span class="bu">sum</span>(problem.bedarf)<span class="sc">}</span><span class="ss"> Paletten Bedarf.&quot;</span>)</span>
<span id="cb10-272"><a href="#cb10-272" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Kapazitaet je Lager: </span><span class="sc">{</span>problem<span class="sc">.</span>kapazitaet[<span class="dv">0</span>]<span class="sc">}</span><span class="ss"> Paletten &quot;</span></span>
<span id="cb10-273"><a href="#cb10-273" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;-&gt; mindestens 3 Lager noetig.</span><span class="ch">\n</span><span class="ss">&quot;</span>)</span>
<span id="cb10-274"><a href="#cb10-274" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-275"><a href="#cb10-275" aria-hidden="true" tabindex="-1"></a> loesungen: <span class="bu">dict</span>[<span class="bu">str</span>, Loesung] <span class="op">=</span> {}</span>
<span id="cb10-276"><a href="#cb10-276" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, beschriftung <span class="kw">in</span> [(<span class="st">&quot;cpsat&quot;</span>, <span class="st">&quot;OR-Tools CP-SAT&quot;</span>),</span>
<span id="cb10-277"><a href="#cb10-277" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;highs&quot;</span>, <span class="st">&quot;HiGHS (highspy)&quot;</span>)]:</span>
<span id="cb10-278"><a href="#cb10-278" aria-hidden="true" tabindex="-1"></a> loesung <span class="op">=</span> loesungen[name] <span class="op">=</span> loese_in_eigenem_prozess(name, problem)</span>
<span id="cb10-279"><a href="#cb10-279" aria-hidden="true" tabindex="-1"></a> beanstandungen <span class="op">=</span> pruefe_zuordnung(problem, loesung)</span>
<span id="cb10-280"><a href="#cb10-280" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-281"><a href="#cb10-281" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>beschriftung<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-282"><a href="#cb10-282" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span>loesung<span class="sc">.</span>als_bericht()<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-283"><a href="#cb10-283" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; eroeffnete Lager: </span><span class="sc">{</span><span class="st">&#39;, &#39;</span><span class="sc">.</span>join(geoeffnete_lager(problem, loesung))<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-284"><a href="#cb10-284" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Abnahmepruefung: &quot;</span></span>
<span id="cb10-285"><a href="#cb10-285" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;bestanden&#39;</span> <span class="cf">if</span> <span class="kw">not</span> beanstandungen <span class="cf">else</span> beanstandungen<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-286"><a href="#cb10-286" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-287"><a href="#cb10-287" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Was der Vergleich zeigt -----------------------------------------</span></span>
<span id="cb10-288"><a href="#cb10-288" aria-hidden="true" tabindex="-1"></a> zielwerte <span class="op">=</span> [loesung.zielwert <span class="cf">for</span> loesung <span class="kw">in</span> loesungen.values()]</span>
<span id="cb10-289"><a href="#cb10-289" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb10-290"><a href="#cb10-290" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Zielwertdifferenz: </span><span class="sc">{</span><span class="bu">abs</span>(zielwerte[<span class="dv">0</span>] <span class="op">-</span> zielwerte[<span class="dv">1</span>])<span class="sc">:.6f}</span><span class="ss"> EUR&quot;</span>)</span>
<span id="cb10-291"><a href="#cb10-291" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-292"><a href="#cb10-292" aria-hidden="true" tabindex="-1"></a> gleich_belegt <span class="op">=</span> <span class="bu">all</span>(</span>
<span id="cb10-293"><a href="#cb10-293" aria-hidden="true" tabindex="-1"></a> <span class="bu">round</span>(loesungen[<span class="st">&quot;cpsat&quot;</span>].werte[s]) <span class="op">==</span> <span class="bu">round</span>(loesungen[<span class="st">&quot;highs&quot;</span>].werte[s])</span>
<span id="cb10-294"><a href="#cb10-294" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> s <span class="kw">in</span> loesungen[<span class="st">&quot;cpsat&quot;</span>].werte)</span>
<span id="cb10-295"><a href="#cb10-295" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Identische Zuordnung: </span><span class="sc">{</span><span class="st">&#39;ja&#39;</span> <span class="cf">if</span> gleich_belegt <span class="cf">else</span> <span class="st">&#39;nein&#39;</span><span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-296"><a href="#cb10-296" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-297"><a href="#cb10-297" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> <span class="bu">abs</span>(zielwerte[<span class="dv">0</span>] <span class="op">-</span> zielwerte[<span class="dv">1</span>]) <span class="op">&lt;</span> <span class="fl">0.5</span>, <span class="st">&quot;Die Solver widersprechen sich!&quot;</span></span>
<span id="cb10-298"><a href="#cb10-298" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> <span class="bu">all</span>(l.status <span class="kw">is</span> SolverStatus.OPTIMAL <span class="cf">for</span> l <span class="kw">in</span> loesungen.values())</span>
<span id="cb10-299"><a href="#cb10-299" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-300"><a href="#cb10-300" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb10-301"><a href="#cb10-301" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; WAS DER WECHSEL GEKOSTET HAT&quot;</span>)</span>
<span id="cb10-302"><a href="#cb10-302" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb10-303"><a href="#cb10-303" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Ausgetauscht wurde EINE Funktion. Domaenenmodell, Abnahmepruefung und&quot;</span>)</span>
<span id="cb10-304"><a href="#cb10-304" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Bericht sind woertlich dieselben - sie sehen den Solver nie.&quot;</span>)</span>
<span id="cb10-305"><a href="#cb10-305" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
<span id="cb10-306"><a href="#cb10-306" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Nicht umsonst ist der Wechsel trotzdem:&quot;</span>)</span>
<span id="cb10-307"><a href="#cb10-307" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; * CP-SAT rechnet ausschliesslich GANZZAHLIG. Alle Kosten sind hier&quot;</span>)</span>
<span id="cb10-308"><a href="#cb10-308" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; deshalb int. Wer in Euro und Cent rechnet, skaliert vorher auf Cent -&quot;</span>)</span>
<span id="cb10-309"><a href="#cb10-309" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; und muss das im Bericht wieder zuruecknehmen.&quot;</span>)</span>
<span id="cb10-310"><a href="#cb10-310" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; * HiGHS braucht die Restriktionen als Matrixzeilen, CP-SAT nimmt sie&quot;</span>)</span>
<span id="cb10-311"><a href="#cb10-311" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; als Ausdruecke. Das ist der Grund, warum der HiGHS-Modellbauer&quot;</span>)</span>
<span id="cb10-312"><a href="#cb10-312" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; laenger ist, obwohl er dasselbe Modell beschreibt.&quot;</span>)</span>
<span id="cb10-313"><a href="#cb10-313" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; * Beide Bibliotheken bringen eine eigene HiGHS-Kopie mit und lassen&quot;</span>)</span>
<span id="cb10-314"><a href="#cb10-314" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; sich nicht gemeinsam importieren - daher die zwei Prozesse.&quot;</span>)</span>
<span id="cb10-315"><a href="#cb10-315" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
<span id="cb10-316"><a href="#cb10-316" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Verglichen wird deshalb der ZIELWERT, nicht der Plan: Gibt es mehrere&quot;</span>)</span>
<span id="cb10-317"><a href="#cb10-317" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;gleich teure Loesungen, darf jeder Solver eine andere davon liefern.&quot;</span>)</span>
<span id="cb10-318"><a href="#cb10-318" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Hier stimmen sie zufaellig ueberein - darauf zu testen waere trotzdem&quot;</span>)</span>
<span id="cb10-319"><a href="#cb10-319" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;ein unzuverlaessiger Test (siehe JobShop_Intervalle.py).&quot;</span>)</span>
<span id="cb10-320"><a href="#cb10-320" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span></code></pre></div>
<span id="cb10-316"><a href="#cb10-316" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Der Ertrag: Beide beweisen denselben optimalen Zielwert, und die&quot;</span>)</span>
<span id="cb10-317"><a href="#cb10-317" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Entscheidung zwischen ihnen ist eine Frage der Laufzeit geworden -&quot;</span>)</span>
<span id="cb10-318"><a href="#cb10-318" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;nicht eine Frage, wie viel Code man neu schreiben muss.&quot;</span>)</span>
<span id="cb10-319"><a href="#cb10-319" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
<span id="cb10-320"><a href="#cb10-320" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Verglichen wird deshalb der ZIELWERT, nicht der Plan: Gibt es mehrere&quot;</span>)</span>
<span id="cb10-321"><a href="#cb10-321" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;gleich teure Loesungen, darf jeder Solver eine andere davon liefern.&quot;</span>)</span>
<span id="cb10-322"><a href="#cb10-322" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Hier stimmen sie zufaellig ueberein - darauf zu testen waere trotzdem&quot;</span>)</span>
<span id="cb10-323"><a href="#cb10-323" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;ein unzuverlaessiger Test (siehe JobShop_Intervalle.py).&quot;</span>)</span>
<span id="cb10-324"><a href="#cb10-324" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span></code></pre></div>
<p><strong>Erwartete Ausgabe</strong> (Laufzeiten hardwareabhängig):</p>
<pre><code>==================================================================================
DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE