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
|
|
@ -115,7 +115,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Ein System — vier Programmieransätze\n",
|
||||
"## Wie die Isolation aussieht, wenn sie tragen soll\n",
|
||||
"\n",
|
||||
"`Ein_System_Vier_Ansaetze.py`\n"
|
||||
]
|
||||
|
|
@ -136,85 +136,104 @@
|
|||
" 2*x1 + 3*x2 + x3 <= 50\n",
|
||||
" x >= 0\n",
|
||||
"\n",
|
||||
"Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich am Ende.\n",
|
||||
"Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich\n",
|
||||
"am Ende.\n",
|
||||
"\n",
|
||||
"WICHTIG: Jeder Solver läuft in einem EIGENEN Prozess, weil sich ortools und\n",
|
||||
"WICHTIG: Jeder Solver laeuft in einem EIGENEN Prozess, weil sich ortools und\n",
|
||||
"highspy auf vielen Systemen nicht gemeinsam importieren lassen (beide bringen\n",
|
||||
"eine eigene HiGHS-Kopie mit -> Symbolkonflikt).\n",
|
||||
"\n",
|
||||
"Die Isolation besorgt ein ProcessPoolExecutor. Drei Einstellungen ergeben\n",
|
||||
"zusammen die Garantie:\n",
|
||||
"\n",
|
||||
" mp_context \"spawn\" Der Kindprozess startet mit einem FRISCHEN\n",
|
||||
" Interpreter, statt den Speicher des Elternprozesses\n",
|
||||
" zu erben. Was hier schon importiert ist, ist dort\n",
|
||||
" nicht importiert. Mit dem Standard \"fork\" auf Linux\n",
|
||||
" waere das nicht so.\n",
|
||||
" max_tasks_per_child=1 Jede Aufgabe bekommt einen NEUEN Prozess. Ohne das\n",
|
||||
" wuerde der Pool seinen Arbeiter wiederverwenden - und\n",
|
||||
" beim zweiten Solver waere der Konflikt zurueck.\n",
|
||||
" max_workers=1 Haelt die vier Laeufe nacheinander. Nicht aus\n",
|
||||
" Vorsicht, sondern damit die gemessenen Zeiten\n",
|
||||
" vergleichbar bleiben.\n",
|
||||
"\n",
|
||||
"Jeder Solver steht in einer eigenen Funktion mit LOKALEM Import. Das ist der\n",
|
||||
"Unterschied zu einem Codestring, den man an 'python -c' uebergibt: Die\n",
|
||||
"Funktion laesst sich einzeln aufrufen, testen und vom Editor pruefen - ein\n",
|
||||
"String nicht.\n",
|
||||
"\n",
|
||||
"Benoetigt: scipy, highspy, cvxpy, ortools\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"import json\n",
|
||||
"import subprocess\n",
|
||||
"import sys\n",
|
||||
"import textwrap\n",
|
||||
"import multiprocessing\n",
|
||||
"import time\n",
|
||||
"from concurrent.futures import ProcessPoolExecutor\n",
|
||||
"\n",
|
||||
"ERWARTET = 530.0 # Ergebnis der Handrechnung zum Produktionsprogramm\n",
|
||||
"\n",
|
||||
"# Jeder Eintrag ist ein eigenständiges Miniprogramm, das sein Ergebnis als\n",
|
||||
"# JSON auf stdout ausgibt. So bleibt jeder Import in seinem eigenen Prozess.\n",
|
||||
"ANSAETZE: dict[str, str] = {\n",
|
||||
"# Die Instanz - einmal notiert, von allen vier Funktionen benutzt.\n",
|
||||
"ZIEL = [10.0, 15.0, 25.0]\n",
|
||||
"MATRIX = [[1, 1, 2], [2, 3, 1]]\n",
|
||||
"KAPAZITAET = [40.0, 50.0]\n",
|
||||
"\n",
|
||||
" \"scipy.optimize.linprog\": \"\"\"\n",
|
||||
"\n",
|
||||
"def loese_mit_scipy() -> tuple[float, list[float]]:\n",
|
||||
" from scipy.optimize import linprog\n",
|
||||
" res = linprog(c=[-10.0, -15.0, -25.0], # linprog MINIMIERT -> negieren\n",
|
||||
" A_ub=[[1, 1, 2], [2, 3, 1]], b_ub=[40, 50],\n",
|
||||
" ergebnis = linprog(c=[-w for w in ZIEL], # linprog MINIMIERT -> negieren\n",
|
||||
" A_ub=MATRIX, b_ub=KAPAZITAET,\n",
|
||||
" bounds=[(0, None)] * 3, method=\"highs\")\n",
|
||||
" ausgabe = (-res.fun, list(res.x))\n",
|
||||
" \"\"\",\n",
|
||||
" return -ergebnis.fun, list(ergebnis.x)\n",
|
||||
"\n",
|
||||
" \"highspy (natives HiGHS)\": \"\"\"\n",
|
||||
" import numpy as np, highspy\n",
|
||||
"\n",
|
||||
"def loese_mit_highspy() -> tuple[float, list[float]]:\n",
|
||||
" import highspy\n",
|
||||
" import numpy as np\n",
|
||||
" h = highspy.Highs()\n",
|
||||
" h.setOptionValue(\"output_flag\", False)\n",
|
||||
" h.addVars(3, np.zeros(3), np.full(3, highspy.kHighsInf))\n",
|
||||
" h.changeObjectiveSense(highspy.ObjSense.kMaximize)\n",
|
||||
" for j, wert in enumerate([10.0, 15.0, 25.0]):\n",
|
||||
" for j, wert in enumerate(ZIEL):\n",
|
||||
" h.changeColCost(j, wert)\n",
|
||||
" # CSR-Format: starts[i] = Beginn von Zeile i in indices/values\n",
|
||||
" h.addRows(2, np.full(2, -highspy.kHighsInf), np.array([40.0, 50.0]), 6,\n",
|
||||
" h.addRows(2, np.full(2, -highspy.kHighsInf), np.array(KAPAZITAET), 6,\n",
|
||||
" np.array([0, 3], dtype=np.int32),\n",
|
||||
" np.array([0, 1, 2, 0, 1, 2], dtype=np.int32),\n",
|
||||
" np.array([1.0, 1.0, 2.0, 2.0, 3.0, 1.0]))\n",
|
||||
" np.array([float(w) for zeile in MATRIX for w in zeile]))\n",
|
||||
" h.run()\n",
|
||||
" ausgabe = (h.getInfo().objective_function_value,\n",
|
||||
" return (h.getInfo().objective_function_value,\n",
|
||||
" list(h.getSolution().col_value[:3]))\n",
|
||||
" \"\"\",\n",
|
||||
"\n",
|
||||
" \"cvxpy\": \"\"\"\n",
|
||||
" import numpy as np, cvxpy as cp\n",
|
||||
"\n",
|
||||
"def loese_mit_cvxpy() -> tuple[float, list[float]]:\n",
|
||||
" import cvxpy as cp\n",
|
||||
" import numpy as np\n",
|
||||
" x = cp.Variable(3, nonneg=True)\n",
|
||||
" problem = cp.Problem(cp.Maximize(np.array([10.0, 15.0, 25.0]) @ x),\n",
|
||||
" [np.array([[1, 1, 2], [2, 3, 1]]) @ x <= np.array([40, 50])])\n",
|
||||
" problem = cp.Problem(cp.Maximize(np.array(ZIEL) @ x),\n",
|
||||
" [np.array(MATRIX) @ x <= np.array(KAPAZITAET)])\n",
|
||||
" problem.solve()\n",
|
||||
" ausgabe = (float(problem.value), [float(v) for v in x.value])\n",
|
||||
" \"\"\",\n",
|
||||
" return float(problem.value), [float(v) for v in x.value]\n",
|
||||
"\n",
|
||||
" \"ortools / GLOP\": \"\"\"\n",
|
||||
"\n",
|
||||
"def loese_mit_ortools() -> tuple[float, list[float]]:\n",
|
||||
" from ortools.linear_solver import pywraplp\n",
|
||||
" s = pywraplp.Solver.CreateSolver(\"GLOP\")\n",
|
||||
" x = [s.NumVar(0, s.infinity(), f\"x{j+1}\") for j in range(3)]\n",
|
||||
" A = [[1, 1, 2], [2, 3, 1]]\n",
|
||||
" for i, kap in enumerate([40, 50]):\n",
|
||||
" s.Add(sum(A[i][j] * x[j] for j in range(3)) <= kap)\n",
|
||||
" s.Maximize(10 * x[0] + 15 * x[1] + 25 * x[2])\n",
|
||||
" for i, kapazitaet in enumerate(KAPAZITAET):\n",
|
||||
" s.Add(sum(MATRIX[i][j] * x[j] for j in range(3)) <= kapazitaet)\n",
|
||||
" s.Maximize(sum(ZIEL[j] * x[j] for j in range(3)))\n",
|
||||
" s.Solve()\n",
|
||||
" ausgabe = (s.Objective().Value(), [v.solution_value() for v in x])\n",
|
||||
" \"\"\",\n",
|
||||
" return s.Objective().Value(), [v.solution_value() for v in x]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"ANSAETZE = {\n",
|
||||
" \"scipy.optimize.linprog\": loese_mit_scipy,\n",
|
||||
" \"highspy (natives HiGHS)\": loese_mit_highspy,\n",
|
||||
" \"cvxpy\": loese_mit_cvxpy,\n",
|
||||
" \"ortools / GLOP\": loese_mit_ortools,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def fuehre_in_eigenem_prozess_aus(quelltext: str) -> tuple[float, list[float]]:\n",
|
||||
" \"\"\"Startet den Codeschnipsel als separaten Python-Prozess und liest das Ergebnis.\"\"\"\n",
|
||||
" programm = textwrap.dedent(quelltext) + \"\\nimport json; print(json.dumps(ausgabe))\\n\"\n",
|
||||
" ergebnis = subprocess.run([sys.executable, \"-c\", programm],\n",
|
||||
" capture_output=True, text=True, timeout=120)\n",
|
||||
" if ergebnis.returncode != 0:\n",
|
||||
" raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])\n",
|
||||
" wert, loesung = json.loads(ergebnis.stdout.strip().splitlines()[-1])\n",
|
||||
" return wert, loesung\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" print(\"=\" * 78)\n",
|
||||
" print(\" EIN SYSTEM - VIER ANSAETZE (je eigener Prozess)\")\n",
|
||||
|
|
@ -223,14 +242,20 @@
|
|||
" print(\"-\" * 78)\n",
|
||||
"\n",
|
||||
" werte = []\n",
|
||||
" for name, quelltext in ANSAETZE.items():\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" # Ein Pool, vier Aufgaben, vier frische Prozesse. Der Kontext muss\n",
|
||||
" # \"spawn\" sein - siehe Modulkommentar.\n",
|
||||
" with ProcessPoolExecutor(\n",
|
||||
" max_workers=1,\n",
|
||||
" mp_context=multiprocessing.get_context(\"spawn\"),\n",
|
||||
" max_tasks_per_child=1) as pool:\n",
|
||||
" for name, funktion in ANSAETZE.items():\n",
|
||||
" beginn = time.perf_counter()\n",
|
||||
" try:\n",
|
||||
" wert, x = fuehre_in_eigenem_prozess_aus(quelltext)\n",
|
||||
" except RuntimeError as fehler:\n",
|
||||
" print(f\"{name:<26} nicht verfuegbar: {fehler[:40]}\")\n",
|
||||
" wert, x = pool.submit(funktion).result(timeout=120)\n",
|
||||
" except Exception as fehler: # Bibliothek fehlt o. Ae.\n",
|
||||
" print(f\"{name:<26} nicht verfuegbar: {str(fehler)[:40]}\")\n",
|
||||
" continue\n",
|
||||
" dauer = time.perf_counter() - t0\n",
|
||||
" dauer = time.perf_counter() - beginn\n",
|
||||
" werte.append(wert)\n",
|
||||
" print(f\"{name:<26} {wert:>10.2f} {x[0]:>7.2f} {x[1]:>7.2f} {x[2]:>7.2f} \"\n",
|
||||
" f\"{dauer:>8.2f} s\")\n",
|
||||
|
|
@ -243,8 +268,8 @@
|
|||
" assert spanne < 1e-6, \"Die Bibliotheken widersprechen sich!\"\n",
|
||||
" assert abs(werte[0] - ERWARTET) < 1e-6, \"Ergebnis weicht von der Handrechnung ab!\"\n",
|
||||
" print(\"Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.\")\n",
|
||||
" print(\"(Die Zeiten enthalten den Prozessstart und den Import - sie messen\")\n",
|
||||
" print(\" NICHT die reine Solverleistung, siehe Uebung 3.5.)\")\n",
|
||||
" print(\"(Die Zeiten enthalten Prozessstart und Import - sie messen NICHT die\")\n",
|
||||
" print(\" reine Solverleistung. Die Uebungsaufgabe 'Laufzeitvergleich' trennt beides.)\")\n",
|
||||
" print(\"=\" * 78)"
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1148,9 +1148,9 @@
|
|||
"\n",
|
||||
"from __future__ import annotations\n",
|
||||
"\n",
|
||||
"import subprocess\n",
|
||||
"import sys\n",
|
||||
"import multiprocessing\n",
|
||||
"import time\n",
|
||||
"from concurrent.futures import ProcessPoolExecutor\n",
|
||||
"\n",
|
||||
"import numpy as np\n",
|
||||
"from pydantic import BaseModel, Field, model_validator\n",
|
||||
|
|
@ -1353,25 +1353,29 @@
|
|||
" if any(loesung.werte[problem.schluessel(i, j)] > 0.5 for j in range(m))]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def loese_in_eigenem_prozess(name: str) -> Loesung:\n",
|
||||
" \"\"\"Startet dieses Programm noch einmal - mit genau einem Solverimport.\"\"\"\n",
|
||||
" ergebnis = subprocess.run([sys.executable, __file__, name],\n",
|
||||
" capture_output=True, text=True, timeout=300)\n",
|
||||
" if ergebnis.returncode != 0:\n",
|
||||
" raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])\n",
|
||||
" # Das DTO als JSON - genau dafuer ist ein Datenobjekt ohne Solverbezug gut.\n",
|
||||
" return Loesung.model_validate_json(ergebnis.stdout.strip().splitlines()[-1])\n",
|
||||
"def loese_in_eigenem_prozess(name: str, problem: Standortproblem) -> Loesung:\n",
|
||||
" \"\"\"Laesst genau einen Modellbauer in einem frischen Prozess rechnen.\n",
|
||||
"\n",
|
||||
" 'spawn' statt des Linux-Standards 'fork': Der Kindprozess startet mit\n",
|
||||
" einem leeren Interpreter und importiert nur den Solver, den SEIN\n",
|
||||
" Modellbauer braucht. max_tasks_per_child=1 sorgt dafuer, dass der Pool\n",
|
||||
" seinen Arbeiter nicht wiederverwendet - sonst saessen beim zweiten Aufruf\n",
|
||||
" wieder beide Bibliotheken im selben Prozess.\n",
|
||||
"\n",
|
||||
" Hin und zurueck wandert das Domaenenmodell bzw. das Loesungs-DTO. Beide\n",
|
||||
" kennen keinen Solver, sind also serialisierbar - genau dafuer sind sie da.\n",
|
||||
" \"\"\"\n",
|
||||
" with ProcessPoolExecutor(\n",
|
||||
" max_workers=1,\n",
|
||||
" mp_context=multiprocessing.get_context(\"spawn\"),\n",
|
||||
" max_tasks_per_child=1) as pool:\n",
|
||||
" return pool.submit(MODELLBAUER[name], problem).result(timeout=300)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" problem = beispielproblem()\n",
|
||||
"\n",
|
||||
" # --- Kindprozess: rechnen und das DTO als JSON ausgeben ---------------\n",
|
||||
" if len(sys.argv) > 1:\n",
|
||||
" print(MODELLBAUER[sys.argv[1]](problem).model_dump_json())\n",
|
||||
" sys.exit(0)\n",
|
||||
"\n",
|
||||
" # --- Hauptprozess: beide Solver anstossen und vergleichen -------------\n",
|
||||
" # --- Beide Solver anstossen und vergleichen ---------------------------\n",
|
||||
" print(\"=\" * 82)\n",
|
||||
" print(\" DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE\")\n",
|
||||
" print(\"=\" * 82)\n",
|
||||
|
|
@ -1383,7 +1387,7 @@
|
|||
" loesungen: dict[str, Loesung] = {}\n",
|
||||
" for name, beschriftung in [(\"cpsat\", \"OR-Tools CP-SAT\"),\n",
|
||||
" (\"highs\", \"HiGHS (highspy)\")]:\n",
|
||||
" loesung = loesungen[name] = loese_in_eigenem_prozess(name)\n",
|
||||
" loesung = loesungen[name] = loese_in_eigenem_prozess(name, problem)\n",
|
||||
" beanstandungen = pruefe_zuordnung(problem, loesung)\n",
|
||||
"\n",
|
||||
" print(f\"{beschriftung}\")\n",
|
||||
|
|
|
|||
|
|
@ -593,113 +593,124 @@
|
|||
"\n",
|
||||
"from __future__ import annotations\n",
|
||||
"\n",
|
||||
"import json\n",
|
||||
"import subprocess\n",
|
||||
"import sys\n",
|
||||
"import textwrap\n",
|
||||
"import multiprocessing\n",
|
||||
"import resource\n",
|
||||
"import time\n",
|
||||
"from concurrent.futures import ProcessPoolExecutor\n",
|
||||
"\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"GROESSEN = [(10, 10), (32, 32), (100, 100)] # (Lager, Kunden) -> 100 / 1.024 / 10.000 Variablen\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Jeder Eintrag ist ein eigenstaendiges Programm: Instanz aufbauen, loesen,\n",
|
||||
"# Ergebnis als JSON ausgeben. Die Instanz wird in jedem Kindprozess aus\n",
|
||||
"# derselben Saat neu erzeugt - so reist nichts ueber die Prozessgrenze,\n",
|
||||
"# was das Ergebnis verfaelschen koennte.\n",
|
||||
"VORSPANN = \"\"\"\n",
|
||||
"import json, time, resource\n",
|
||||
"import numpy as np\n",
|
||||
"# Instanz und Speichermessung stehen als gewoehnliche Funktionen hier - nicht\n",
|
||||
"# in einem String, den ein Kindprozess ausfuehrt. Jede Messfunktion baut die\n",
|
||||
"# Instanz aus derselben Saat neu auf, damit ueber die Prozessgrenze nichts\n",
|
||||
"# reist, was das Ergebnis verfaelschen koennte.\n",
|
||||
"\n",
|
||||
"def instanz(m, n):\n",
|
||||
"def instanz(m: int, n: int):\n",
|
||||
" rng = np.random.default_rng(20)\n",
|
||||
" kosten = rng.integers(5, 95, (m, n)).astype(float)\n",
|
||||
" angebot = rng.integers(50, 150, m).astype(float)\n",
|
||||
" bedarf = angebot.sum() * rng.dirichlet(np.ones(n))\n",
|
||||
" return kosten, angebot, bedarf\n",
|
||||
"\n",
|
||||
"def speicher_mb():\n",
|
||||
" # ru_maxrss ist unter Linux in Kilobyte\n",
|
||||
"\n",
|
||||
"def speicher_mb() -> float:\n",
|
||||
" # ru_maxrss ist unter Linux in Kilobyte. Gemessen wird der Kindprozess -\n",
|
||||
" # deshalb muss jede Messung einen eigenen bekommen.\n",
|
||||
" return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024\n",
|
||||
"\n",
|
||||
"M, N = {m}, {n}\n",
|
||||
"kosten, angebot, bedarf = instanz(M, N)\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"ANSAETZE = {\n",
|
||||
" \"scipy.linprog\": \"\"\"\n",
|
||||
"def messe_scipy(m: int, n: int):\n",
|
||||
" from scipy.optimize import linprog\n",
|
||||
" kosten, angebot, bedarf = instanz(m, n)\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" c = kosten.reshape(-1)\n",
|
||||
" A_ub = np.zeros((M, M * N)); A_eq = np.zeros((N, M * N))\n",
|
||||
" for i in range(M):\n",
|
||||
" A_ub[i, i * N:(i + 1) * N] = 1.0\n",
|
||||
" for j in range(N):\n",
|
||||
" A_eq[j, j::N] = 1.0\n",
|
||||
" A_ub = np.zeros((m, m * n)); A_eq = np.zeros((n, m * n))\n",
|
||||
" for i in range(m):\n",
|
||||
" A_ub[i, i * n:(i + 1) * n] = 1.0\n",
|
||||
" for j in range(n):\n",
|
||||
" A_eq[j, j::n] = 1.0\n",
|
||||
" aufbau = time.perf_counter() - t0\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" r = linprog(c=c, A_ub=A_ub, b_ub=angebot, A_eq=A_eq, b_eq=bedarf,\n",
|
||||
" bounds=(0, None), method=\"highs\")\n",
|
||||
" loesen = time.perf_counter() - t0\n",
|
||||
" ausgabe = (float(r.fun), aufbau, loesen, speicher_mb())\n",
|
||||
" \"\"\",\n",
|
||||
" return float(r.fun), aufbau, loesen, speicher_mb()\n",
|
||||
"\n",
|
||||
" \"highspy\": \"\"\"\n",
|
||||
"\n",
|
||||
"def messe_highspy(m: int, n: int):\n",
|
||||
" import highspy\n",
|
||||
" kosten, angebot, bedarf = instanz(m, n)\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" h = highspy.Highs(); h.setOptionValue(\"output_flag\", False)\n",
|
||||
" h.addVars(M * N, np.zeros(M * N), np.full(M * N, highspy.kHighsInf))\n",
|
||||
" for k in range(M * N):\n",
|
||||
" h.addVars(m * n, np.zeros(m * n), np.full(m * n, highspy.kHighsInf))\n",
|
||||
" for k in range(m * n):\n",
|
||||
" h.changeColCost(k, float(kosten.reshape(-1)[k]))\n",
|
||||
" for i in range(M):\n",
|
||||
" idx = np.arange(i * N, (i + 1) * N, dtype=np.int32)\n",
|
||||
" h.addRow(-highspy.kHighsInf, float(angebot[i]), N, idx, np.ones(N))\n",
|
||||
" for j in range(N):\n",
|
||||
" idx = np.arange(j, M * N, N, dtype=np.int32)\n",
|
||||
" h.addRow(float(bedarf[j]), float(bedarf[j]), M, idx, np.ones(M))\n",
|
||||
" for i in range(m):\n",
|
||||
" idx = np.arange(i * n, (i + 1) * n, dtype=np.int32)\n",
|
||||
" h.addRow(-highspy.kHighsInf, float(angebot[i]), n, idx, np.ones(n))\n",
|
||||
" for j in range(n):\n",
|
||||
" idx = np.arange(j, m * n, n, dtype=np.int32)\n",
|
||||
" h.addRow(float(bedarf[j]), float(bedarf[j]), m, idx, np.ones(m))\n",
|
||||
" aufbau = time.perf_counter() - t0\n",
|
||||
" t0 = time.perf_counter(); h.run(); loesen = time.perf_counter() - t0\n",
|
||||
" ausgabe = (h.getInfo().objective_function_value, aufbau, loesen, speicher_mb())\n",
|
||||
" \"\"\",\n",
|
||||
" return h.getInfo().objective_function_value, aufbau, loesen, speicher_mb()\n",
|
||||
"\n",
|
||||
" \"ortools/GLOP\": \"\"\"\n",
|
||||
"\n",
|
||||
"def messe_ortools(m: int, n: int):\n",
|
||||
" from ortools.linear_solver import pywraplp\n",
|
||||
" kosten, angebot, bedarf = instanz(m, n)\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" s = pywraplp.Solver.CreateSolver(\"GLOP\")\n",
|
||||
" x = [[s.NumVar(0, s.infinity(), f\"x{i}_{j}\") for j in range(N)]\n",
|
||||
" for i in range(M)]\n",
|
||||
" for i in range(M):\n",
|
||||
" x = [[s.NumVar(0, s.infinity(), f\"x{i}_{j}\") for j in range(n)]\n",
|
||||
" for i in range(m)]\n",
|
||||
" for i in range(m):\n",
|
||||
" s.Add(sum(x[i]) <= float(angebot[i]))\n",
|
||||
" for j in range(N):\n",
|
||||
" s.Add(sum(x[i][j] for i in range(M)) == float(bedarf[j]))\n",
|
||||
" for j in range(n):\n",
|
||||
" s.Add(sum(x[i][j] for i in range(m)) == float(bedarf[j]))\n",
|
||||
" s.Minimize(sum(float(kosten[i, j]) * x[i][j]\n",
|
||||
" for i in range(M) for j in range(N)))\n",
|
||||
" for i in range(m) for j in range(n)))\n",
|
||||
" aufbau = time.perf_counter() - t0\n",
|
||||
" t0 = time.perf_counter(); s.Solve(); loesen = time.perf_counter() - t0\n",
|
||||
" ausgabe = (s.Objective().Value(), aufbau, loesen, speicher_mb())\n",
|
||||
" \"\"\",\n",
|
||||
" return s.Objective().Value(), aufbau, loesen, speicher_mb()\n",
|
||||
"\n",
|
||||
" \"cvxpy\": \"\"\"\n",
|
||||
"\n",
|
||||
"def messe_cvxpy(m: int, n: int):\n",
|
||||
" import cvxpy as cp\n",
|
||||
" kosten, angebot, bedarf = instanz(m, n)\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" x = cp.Variable((M, N), nonneg=True)\n",
|
||||
" x = cp.Variable((m, n), nonneg=True)\n",
|
||||
" problem = cp.Problem(cp.Minimize(cp.sum(cp.multiply(kosten, x))),\n",
|
||||
" [cp.sum(x, axis=1) <= angebot,\n",
|
||||
" cp.sum(x, axis=0) == bedarf])\n",
|
||||
" aufbau = time.perf_counter() - t0\n",
|
||||
" t0 = time.perf_counter(); problem.solve(); loesen = time.perf_counter() - t0\n",
|
||||
" ausgabe = (float(problem.value), aufbau, loesen, speicher_mb())\n",
|
||||
" \"\"\",\n",
|
||||
"}\n",
|
||||
" return float(problem.value), aufbau, loesen, speicher_mb()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def messe(name: str, quelltext: str, m: int, n: int):\n",
|
||||
" \"\"\"Fuehrt einen Ansatz in einem eigenen Prozess aus.\"\"\"\n",
|
||||
" programm = (VORSPANN.format(m=m, n=n) + textwrap.dedent(quelltext)\n",
|
||||
" + \"\\nprint(json.dumps(ausgabe))\\n\")\n",
|
||||
" ergebnis = subprocess.run([sys.executable, \"-c\", programm],\n",
|
||||
" capture_output=True, text=True, timeout=600)\n",
|
||||
" if ergebnis.returncode != 0:\n",
|
||||
" return None, ergebnis.stderr.strip().splitlines()[-1][:60]\n",
|
||||
" return json.loads(ergebnis.stdout.strip().splitlines()[-1]), None\n",
|
||||
"ANSAETZE = {\"scipy.linprog\": messe_scipy, \"highspy\": messe_highspy,\n",
|
||||
" \"ortools/GLOP\": messe_ortools, \"cvxpy\": messe_cvxpy}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def messe(funktion, m: int, n: int):\n",
|
||||
" \"\"\"Fuehrt eine Messfunktion in einem FRISCHEN Prozess aus.\n",
|
||||
"\n",
|
||||
" 'spawn' und max_tasks_per_child=1 zusammen garantieren, was Regel 1\n",
|
||||
" verlangt: Jede Messung sieht einen leeren Interpreter. Ohne das\n",
|
||||
" zweite wuerde der Pool seinen Arbeiter wiederverwenden - dann waere\n",
|
||||
" der Speicherwert der zweiten Bibliothek um die erste zu hoch, und\n",
|
||||
" ortools und highspy saessen im selben Prozess.\n",
|
||||
" \"\"\"\n",
|
||||
" with ProcessPoolExecutor(\n",
|
||||
" max_workers=1,\n",
|
||||
" mp_context=multiprocessing.get_context(\"spawn\"),\n",
|
||||
" max_tasks_per_child=1) as pool:\n",
|
||||
" try:\n",
|
||||
" return pool.submit(funktion, m, n).result(timeout=600), None\n",
|
||||
" except Exception as fehler:\n",
|
||||
" return None, str(fehler).strip().splitlines()[-1][:60]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
|
|
@ -717,8 +728,8 @@
|
|||
" f\"{'Loesen':>9} {'Anteil':>8} {'Speicher':>10}\")\n",
|
||||
" print(\" \" + \"-\" * 72)\n",
|
||||
" zielwerte = {}\n",
|
||||
" for name, quelltext in ANSAETZE.items():\n",
|
||||
" werte, fehler = messe(name, quelltext, m, n)\n",
|
||||
" for name, funktion in ANSAETZE.items():\n",
|
||||
" werte, fehler = messe(funktion, m, n)\n",
|
||||
" if werte is None:\n",
|
||||
" print(f\" {name:<16} nicht verfuegbar: {fehler}\")\n",
|
||||
" continue\n",
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Ein System — vier Programmieransätze\n",
|
||||
"## Wie die Isolation aussieht, wenn sie tragen soll\n",
|
||||
"\n",
|
||||
"`Ein_System_Vier_Ansaetze.py`\n"
|
||||
]
|
||||
|
|
@ -136,85 +136,104 @@
|
|||
" 2*x1 + 3*x2 + x3 <= 50\n",
|
||||
" x >= 0\n",
|
||||
"\n",
|
||||
"Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich am Ende.\n",
|
||||
"Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich\n",
|
||||
"am Ende.\n",
|
||||
"\n",
|
||||
"WICHTIG: Jeder Solver läuft in einem EIGENEN Prozess, weil sich ortools und\n",
|
||||
"WICHTIG: Jeder Solver laeuft in einem EIGENEN Prozess, weil sich ortools und\n",
|
||||
"highspy auf vielen Systemen nicht gemeinsam importieren lassen (beide bringen\n",
|
||||
"eine eigene HiGHS-Kopie mit -> Symbolkonflikt).\n",
|
||||
"\n",
|
||||
"Die Isolation besorgt ein ProcessPoolExecutor. Drei Einstellungen ergeben\n",
|
||||
"zusammen die Garantie:\n",
|
||||
"\n",
|
||||
" mp_context \"spawn\" Der Kindprozess startet mit einem FRISCHEN\n",
|
||||
" Interpreter, statt den Speicher des Elternprozesses\n",
|
||||
" zu erben. Was hier schon importiert ist, ist dort\n",
|
||||
" nicht importiert. Mit dem Standard \"fork\" auf Linux\n",
|
||||
" waere das nicht so.\n",
|
||||
" max_tasks_per_child=1 Jede Aufgabe bekommt einen NEUEN Prozess. Ohne das\n",
|
||||
" wuerde der Pool seinen Arbeiter wiederverwenden - und\n",
|
||||
" beim zweiten Solver waere der Konflikt zurueck.\n",
|
||||
" max_workers=1 Haelt die vier Laeufe nacheinander. Nicht aus\n",
|
||||
" Vorsicht, sondern damit die gemessenen Zeiten\n",
|
||||
" vergleichbar bleiben.\n",
|
||||
"\n",
|
||||
"Jeder Solver steht in einer eigenen Funktion mit LOKALEM Import. Das ist der\n",
|
||||
"Unterschied zu einem Codestring, den man an 'python -c' uebergibt: Die\n",
|
||||
"Funktion laesst sich einzeln aufrufen, testen und vom Editor pruefen - ein\n",
|
||||
"String nicht.\n",
|
||||
"\n",
|
||||
"Benoetigt: scipy, highspy, cvxpy, ortools\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"import json\n",
|
||||
"import subprocess\n",
|
||||
"import sys\n",
|
||||
"import textwrap\n",
|
||||
"import multiprocessing\n",
|
||||
"import time\n",
|
||||
"from concurrent.futures import ProcessPoolExecutor\n",
|
||||
"\n",
|
||||
"ERWARTET = 530.0 # Ergebnis der Handrechnung zum Produktionsprogramm\n",
|
||||
"\n",
|
||||
"# Jeder Eintrag ist ein eigenständiges Miniprogramm, das sein Ergebnis als\n",
|
||||
"# JSON auf stdout ausgibt. So bleibt jeder Import in seinem eigenen Prozess.\n",
|
||||
"ANSAETZE: dict[str, str] = {\n",
|
||||
"# Die Instanz - einmal notiert, von allen vier Funktionen benutzt.\n",
|
||||
"ZIEL = [10.0, 15.0, 25.0]\n",
|
||||
"MATRIX = [[1, 1, 2], [2, 3, 1]]\n",
|
||||
"KAPAZITAET = [40.0, 50.0]\n",
|
||||
"\n",
|
||||
" \"scipy.optimize.linprog\": \"\"\"\n",
|
||||
"\n",
|
||||
"def loese_mit_scipy() -> tuple[float, list[float]]:\n",
|
||||
" from scipy.optimize import linprog\n",
|
||||
" res = linprog(c=[-10.0, -15.0, -25.0], # linprog MINIMIERT -> negieren\n",
|
||||
" A_ub=[[1, 1, 2], [2, 3, 1]], b_ub=[40, 50],\n",
|
||||
" ergebnis = linprog(c=[-w for w in ZIEL], # linprog MINIMIERT -> negieren\n",
|
||||
" A_ub=MATRIX, b_ub=KAPAZITAET,\n",
|
||||
" bounds=[(0, None)] * 3, method=\"highs\")\n",
|
||||
" ausgabe = (-res.fun, list(res.x))\n",
|
||||
" \"\"\",\n",
|
||||
" return -ergebnis.fun, list(ergebnis.x)\n",
|
||||
"\n",
|
||||
" \"highspy (natives HiGHS)\": \"\"\"\n",
|
||||
" import numpy as np, highspy\n",
|
||||
"\n",
|
||||
"def loese_mit_highspy() -> tuple[float, list[float]]:\n",
|
||||
" import highspy\n",
|
||||
" import numpy as np\n",
|
||||
" h = highspy.Highs()\n",
|
||||
" h.setOptionValue(\"output_flag\", False)\n",
|
||||
" h.addVars(3, np.zeros(3), np.full(3, highspy.kHighsInf))\n",
|
||||
" h.changeObjectiveSense(highspy.ObjSense.kMaximize)\n",
|
||||
" for j, wert in enumerate([10.0, 15.0, 25.0]):\n",
|
||||
" for j, wert in enumerate(ZIEL):\n",
|
||||
" h.changeColCost(j, wert)\n",
|
||||
" # CSR-Format: starts[i] = Beginn von Zeile i in indices/values\n",
|
||||
" h.addRows(2, np.full(2, -highspy.kHighsInf), np.array([40.0, 50.0]), 6,\n",
|
||||
" h.addRows(2, np.full(2, -highspy.kHighsInf), np.array(KAPAZITAET), 6,\n",
|
||||
" np.array([0, 3], dtype=np.int32),\n",
|
||||
" np.array([0, 1, 2, 0, 1, 2], dtype=np.int32),\n",
|
||||
" np.array([1.0, 1.0, 2.0, 2.0, 3.0, 1.0]))\n",
|
||||
" np.array([float(w) for zeile in MATRIX for w in zeile]))\n",
|
||||
" h.run()\n",
|
||||
" ausgabe = (h.getInfo().objective_function_value,\n",
|
||||
" return (h.getInfo().objective_function_value,\n",
|
||||
" list(h.getSolution().col_value[:3]))\n",
|
||||
" \"\"\",\n",
|
||||
"\n",
|
||||
" \"cvxpy\": \"\"\"\n",
|
||||
" import numpy as np, cvxpy as cp\n",
|
||||
"\n",
|
||||
"def loese_mit_cvxpy() -> tuple[float, list[float]]:\n",
|
||||
" import cvxpy as cp\n",
|
||||
" import numpy as np\n",
|
||||
" x = cp.Variable(3, nonneg=True)\n",
|
||||
" problem = cp.Problem(cp.Maximize(np.array([10.0, 15.0, 25.0]) @ x),\n",
|
||||
" [np.array([[1, 1, 2], [2, 3, 1]]) @ x <= np.array([40, 50])])\n",
|
||||
" problem = cp.Problem(cp.Maximize(np.array(ZIEL) @ x),\n",
|
||||
" [np.array(MATRIX) @ x <= np.array(KAPAZITAET)])\n",
|
||||
" problem.solve()\n",
|
||||
" ausgabe = (float(problem.value), [float(v) for v in x.value])\n",
|
||||
" \"\"\",\n",
|
||||
" return float(problem.value), [float(v) for v in x.value]\n",
|
||||
"\n",
|
||||
" \"ortools / GLOP\": \"\"\"\n",
|
||||
"\n",
|
||||
"def loese_mit_ortools() -> tuple[float, list[float]]:\n",
|
||||
" from ortools.linear_solver import pywraplp\n",
|
||||
" s = pywraplp.Solver.CreateSolver(\"GLOP\")\n",
|
||||
" x = [s.NumVar(0, s.infinity(), f\"x{j+1}\") for j in range(3)]\n",
|
||||
" A = [[1, 1, 2], [2, 3, 1]]\n",
|
||||
" for i, kap in enumerate([40, 50]):\n",
|
||||
" s.Add(sum(A[i][j] * x[j] for j in range(3)) <= kap)\n",
|
||||
" s.Maximize(10 * x[0] + 15 * x[1] + 25 * x[2])\n",
|
||||
" for i, kapazitaet in enumerate(KAPAZITAET):\n",
|
||||
" s.Add(sum(MATRIX[i][j] * x[j] for j in range(3)) <= kapazitaet)\n",
|
||||
" s.Maximize(sum(ZIEL[j] * x[j] for j in range(3)))\n",
|
||||
" s.Solve()\n",
|
||||
" ausgabe = (s.Objective().Value(), [v.solution_value() for v in x])\n",
|
||||
" \"\"\",\n",
|
||||
" return s.Objective().Value(), [v.solution_value() for v in x]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"ANSAETZE = {\n",
|
||||
" \"scipy.optimize.linprog\": loese_mit_scipy,\n",
|
||||
" \"highspy (natives HiGHS)\": loese_mit_highspy,\n",
|
||||
" \"cvxpy\": loese_mit_cvxpy,\n",
|
||||
" \"ortools / GLOP\": loese_mit_ortools,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def fuehre_in_eigenem_prozess_aus(quelltext: str) -> tuple[float, list[float]]:\n",
|
||||
" \"\"\"Startet den Codeschnipsel als separaten Python-Prozess und liest das Ergebnis.\"\"\"\n",
|
||||
" programm = textwrap.dedent(quelltext) + \"\\nimport json; print(json.dumps(ausgabe))\\n\"\n",
|
||||
" ergebnis = subprocess.run([sys.executable, \"-c\", programm],\n",
|
||||
" capture_output=True, text=True, timeout=120)\n",
|
||||
" if ergebnis.returncode != 0:\n",
|
||||
" raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])\n",
|
||||
" wert, loesung = json.loads(ergebnis.stdout.strip().splitlines()[-1])\n",
|
||||
" return wert, loesung\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" print(\"=\" * 78)\n",
|
||||
" print(\" EIN SYSTEM - VIER ANSAETZE (je eigener Prozess)\")\n",
|
||||
|
|
@ -223,14 +242,20 @@
|
|||
" print(\"-\" * 78)\n",
|
||||
"\n",
|
||||
" werte = []\n",
|
||||
" for name, quelltext in ANSAETZE.items():\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" # Ein Pool, vier Aufgaben, vier frische Prozesse. Der Kontext muss\n",
|
||||
" # \"spawn\" sein - siehe Modulkommentar.\n",
|
||||
" with ProcessPoolExecutor(\n",
|
||||
" max_workers=1,\n",
|
||||
" mp_context=multiprocessing.get_context(\"spawn\"),\n",
|
||||
" max_tasks_per_child=1) as pool:\n",
|
||||
" for name, funktion in ANSAETZE.items():\n",
|
||||
" beginn = time.perf_counter()\n",
|
||||
" try:\n",
|
||||
" wert, x = fuehre_in_eigenem_prozess_aus(quelltext)\n",
|
||||
" except RuntimeError as fehler:\n",
|
||||
" print(f\"{name:<26} nicht verfuegbar: {fehler[:40]}\")\n",
|
||||
" wert, x = pool.submit(funktion).result(timeout=120)\n",
|
||||
" except Exception as fehler: # Bibliothek fehlt o. Ae.\n",
|
||||
" print(f\"{name:<26} nicht verfuegbar: {str(fehler)[:40]}\")\n",
|
||||
" continue\n",
|
||||
" dauer = time.perf_counter() - t0\n",
|
||||
" dauer = time.perf_counter() - beginn\n",
|
||||
" werte.append(wert)\n",
|
||||
" print(f\"{name:<26} {wert:>10.2f} {x[0]:>7.2f} {x[1]:>7.2f} {x[2]:>7.2f} \"\n",
|
||||
" f\"{dauer:>8.2f} s\")\n",
|
||||
|
|
@ -243,8 +268,8 @@
|
|||
" assert spanne < 1e-6, \"Die Bibliotheken widersprechen sich!\"\n",
|
||||
" assert abs(werte[0] - ERWARTET) < 1e-6, \"Ergebnis weicht von der Handrechnung ab!\"\n",
|
||||
" print(\"Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.\")\n",
|
||||
" print(\"(Die Zeiten enthalten den Prozessstart und den Import - sie messen\")\n",
|
||||
" print(\" NICHT die reine Solverleistung, siehe Uebung 3.5.)\")\n",
|
||||
" print(\"(Die Zeiten enthalten Prozessstart und Import - sie messen NICHT die\")\n",
|
||||
" print(\" reine Solverleistung. Die Uebungsaufgabe 'Laufzeitvergleich' trennt beides.)\")\n",
|
||||
" print(\"=\" * 78)"
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1148,9 +1148,9 @@
|
|||
"\n",
|
||||
"from __future__ import annotations\n",
|
||||
"\n",
|
||||
"import subprocess\n",
|
||||
"import sys\n",
|
||||
"import multiprocessing\n",
|
||||
"import time\n",
|
||||
"from concurrent.futures import ProcessPoolExecutor\n",
|
||||
"\n",
|
||||
"import numpy as np\n",
|
||||
"from pydantic import BaseModel, Field, model_validator\n",
|
||||
|
|
@ -1353,25 +1353,29 @@
|
|||
" if any(loesung.werte[problem.schluessel(i, j)] > 0.5 for j in range(m))]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def loese_in_eigenem_prozess(name: str) -> Loesung:\n",
|
||||
" \"\"\"Startet dieses Programm noch einmal - mit genau einem Solverimport.\"\"\"\n",
|
||||
" ergebnis = subprocess.run([sys.executable, __file__, name],\n",
|
||||
" capture_output=True, text=True, timeout=300)\n",
|
||||
" if ergebnis.returncode != 0:\n",
|
||||
" raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])\n",
|
||||
" # Das DTO als JSON - genau dafuer ist ein Datenobjekt ohne Solverbezug gut.\n",
|
||||
" return Loesung.model_validate_json(ergebnis.stdout.strip().splitlines()[-1])\n",
|
||||
"def loese_in_eigenem_prozess(name: str, problem: Standortproblem) -> Loesung:\n",
|
||||
" \"\"\"Laesst genau einen Modellbauer in einem frischen Prozess rechnen.\n",
|
||||
"\n",
|
||||
" 'spawn' statt des Linux-Standards 'fork': Der Kindprozess startet mit\n",
|
||||
" einem leeren Interpreter und importiert nur den Solver, den SEIN\n",
|
||||
" Modellbauer braucht. max_tasks_per_child=1 sorgt dafuer, dass der Pool\n",
|
||||
" seinen Arbeiter nicht wiederverwendet - sonst saessen beim zweiten Aufruf\n",
|
||||
" wieder beide Bibliotheken im selben Prozess.\n",
|
||||
"\n",
|
||||
" Hin und zurueck wandert das Domaenenmodell bzw. das Loesungs-DTO. Beide\n",
|
||||
" kennen keinen Solver, sind also serialisierbar - genau dafuer sind sie da.\n",
|
||||
" \"\"\"\n",
|
||||
" with ProcessPoolExecutor(\n",
|
||||
" max_workers=1,\n",
|
||||
" mp_context=multiprocessing.get_context(\"spawn\"),\n",
|
||||
" max_tasks_per_child=1) as pool:\n",
|
||||
" return pool.submit(MODELLBAUER[name], problem).result(timeout=300)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" problem = beispielproblem()\n",
|
||||
"\n",
|
||||
" # --- Kindprozess: rechnen und das DTO als JSON ausgeben ---------------\n",
|
||||
" if len(sys.argv) > 1:\n",
|
||||
" print(MODELLBAUER[sys.argv[1]](problem).model_dump_json())\n",
|
||||
" sys.exit(0)\n",
|
||||
"\n",
|
||||
" # --- Hauptprozess: beide Solver anstossen und vergleichen -------------\n",
|
||||
" # --- Beide Solver anstossen und vergleichen ---------------------------\n",
|
||||
" print(\"=\" * 82)\n",
|
||||
" print(\" DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE\")\n",
|
||||
" print(\"=\" * 82)\n",
|
||||
|
|
@ -1383,7 +1387,7 @@
|
|||
" loesungen: dict[str, Loesung] = {}\n",
|
||||
" for name, beschriftung in [(\"cpsat\", \"OR-Tools CP-SAT\"),\n",
|
||||
" (\"highs\", \"HiGHS (highspy)\")]:\n",
|
||||
" loesung = loesungen[name] = loese_in_eigenem_prozess(name)\n",
|
||||
" loesung = loesungen[name] = loese_in_eigenem_prozess(name, problem)\n",
|
||||
" beanstandungen = pruefe_zuordnung(problem, loesung)\n",
|
||||
"\n",
|
||||
" print(f\"{beschriftung}\")\n",
|
||||
|
|
|
|||
|
|
@ -593,113 +593,124 @@
|
|||
"\n",
|
||||
"from __future__ import annotations\n",
|
||||
"\n",
|
||||
"import json\n",
|
||||
"import subprocess\n",
|
||||
"import sys\n",
|
||||
"import textwrap\n",
|
||||
"import multiprocessing\n",
|
||||
"import resource\n",
|
||||
"import time\n",
|
||||
"from concurrent.futures import ProcessPoolExecutor\n",
|
||||
"\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"GROESSEN = [(10, 10), (32, 32), (100, 100)] # (Lager, Kunden) -> 100 / 1.024 / 10.000 Variablen\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Jeder Eintrag ist ein eigenstaendiges Programm: Instanz aufbauen, loesen,\n",
|
||||
"# Ergebnis als JSON ausgeben. Die Instanz wird in jedem Kindprozess aus\n",
|
||||
"# derselben Saat neu erzeugt - so reist nichts ueber die Prozessgrenze,\n",
|
||||
"# was das Ergebnis verfaelschen koennte.\n",
|
||||
"VORSPANN = \"\"\"\n",
|
||||
"import json, time, resource\n",
|
||||
"import numpy as np\n",
|
||||
"# Instanz und Speichermessung stehen als gewoehnliche Funktionen hier - nicht\n",
|
||||
"# in einem String, den ein Kindprozess ausfuehrt. Jede Messfunktion baut die\n",
|
||||
"# Instanz aus derselben Saat neu auf, damit ueber die Prozessgrenze nichts\n",
|
||||
"# reist, was das Ergebnis verfaelschen koennte.\n",
|
||||
"\n",
|
||||
"def instanz(m, n):\n",
|
||||
"def instanz(m: int, n: int):\n",
|
||||
" rng = np.random.default_rng(20)\n",
|
||||
" kosten = rng.integers(5, 95, (m, n)).astype(float)\n",
|
||||
" angebot = rng.integers(50, 150, m).astype(float)\n",
|
||||
" bedarf = angebot.sum() * rng.dirichlet(np.ones(n))\n",
|
||||
" return kosten, angebot, bedarf\n",
|
||||
"\n",
|
||||
"def speicher_mb():\n",
|
||||
" # ru_maxrss ist unter Linux in Kilobyte\n",
|
||||
"\n",
|
||||
"def speicher_mb() -> float:\n",
|
||||
" # ru_maxrss ist unter Linux in Kilobyte. Gemessen wird der Kindprozess -\n",
|
||||
" # deshalb muss jede Messung einen eigenen bekommen.\n",
|
||||
" return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024\n",
|
||||
"\n",
|
||||
"M, N = {m}, {n}\n",
|
||||
"kosten, angebot, bedarf = instanz(M, N)\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"ANSAETZE = {\n",
|
||||
" \"scipy.linprog\": \"\"\"\n",
|
||||
"def messe_scipy(m: int, n: int):\n",
|
||||
" from scipy.optimize import linprog\n",
|
||||
" kosten, angebot, bedarf = instanz(m, n)\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" c = kosten.reshape(-1)\n",
|
||||
" A_ub = np.zeros((M, M * N)); A_eq = np.zeros((N, M * N))\n",
|
||||
" for i in range(M):\n",
|
||||
" A_ub[i, i * N:(i + 1) * N] = 1.0\n",
|
||||
" for j in range(N):\n",
|
||||
" A_eq[j, j::N] = 1.0\n",
|
||||
" A_ub = np.zeros((m, m * n)); A_eq = np.zeros((n, m * n))\n",
|
||||
" for i in range(m):\n",
|
||||
" A_ub[i, i * n:(i + 1) * n] = 1.0\n",
|
||||
" for j in range(n):\n",
|
||||
" A_eq[j, j::n] = 1.0\n",
|
||||
" aufbau = time.perf_counter() - t0\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" r = linprog(c=c, A_ub=A_ub, b_ub=angebot, A_eq=A_eq, b_eq=bedarf,\n",
|
||||
" bounds=(0, None), method=\"highs\")\n",
|
||||
" loesen = time.perf_counter() - t0\n",
|
||||
" ausgabe = (float(r.fun), aufbau, loesen, speicher_mb())\n",
|
||||
" \"\"\",\n",
|
||||
" return float(r.fun), aufbau, loesen, speicher_mb()\n",
|
||||
"\n",
|
||||
" \"highspy\": \"\"\"\n",
|
||||
"\n",
|
||||
"def messe_highspy(m: int, n: int):\n",
|
||||
" import highspy\n",
|
||||
" kosten, angebot, bedarf = instanz(m, n)\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" h = highspy.Highs(); h.setOptionValue(\"output_flag\", False)\n",
|
||||
" h.addVars(M * N, np.zeros(M * N), np.full(M * N, highspy.kHighsInf))\n",
|
||||
" for k in range(M * N):\n",
|
||||
" h.addVars(m * n, np.zeros(m * n), np.full(m * n, highspy.kHighsInf))\n",
|
||||
" for k in range(m * n):\n",
|
||||
" h.changeColCost(k, float(kosten.reshape(-1)[k]))\n",
|
||||
" for i in range(M):\n",
|
||||
" idx = np.arange(i * N, (i + 1) * N, dtype=np.int32)\n",
|
||||
" h.addRow(-highspy.kHighsInf, float(angebot[i]), N, idx, np.ones(N))\n",
|
||||
" for j in range(N):\n",
|
||||
" idx = np.arange(j, M * N, N, dtype=np.int32)\n",
|
||||
" h.addRow(float(bedarf[j]), float(bedarf[j]), M, idx, np.ones(M))\n",
|
||||
" for i in range(m):\n",
|
||||
" idx = np.arange(i * n, (i + 1) * n, dtype=np.int32)\n",
|
||||
" h.addRow(-highspy.kHighsInf, float(angebot[i]), n, idx, np.ones(n))\n",
|
||||
" for j in range(n):\n",
|
||||
" idx = np.arange(j, m * n, n, dtype=np.int32)\n",
|
||||
" h.addRow(float(bedarf[j]), float(bedarf[j]), m, idx, np.ones(m))\n",
|
||||
" aufbau = time.perf_counter() - t0\n",
|
||||
" t0 = time.perf_counter(); h.run(); loesen = time.perf_counter() - t0\n",
|
||||
" ausgabe = (h.getInfo().objective_function_value, aufbau, loesen, speicher_mb())\n",
|
||||
" \"\"\",\n",
|
||||
" return h.getInfo().objective_function_value, aufbau, loesen, speicher_mb()\n",
|
||||
"\n",
|
||||
" \"ortools/GLOP\": \"\"\"\n",
|
||||
"\n",
|
||||
"def messe_ortools(m: int, n: int):\n",
|
||||
" from ortools.linear_solver import pywraplp\n",
|
||||
" kosten, angebot, bedarf = instanz(m, n)\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" s = pywraplp.Solver.CreateSolver(\"GLOP\")\n",
|
||||
" x = [[s.NumVar(0, s.infinity(), f\"x{i}_{j}\") for j in range(N)]\n",
|
||||
" for i in range(M)]\n",
|
||||
" for i in range(M):\n",
|
||||
" x = [[s.NumVar(0, s.infinity(), f\"x{i}_{j}\") for j in range(n)]\n",
|
||||
" for i in range(m)]\n",
|
||||
" for i in range(m):\n",
|
||||
" s.Add(sum(x[i]) <= float(angebot[i]))\n",
|
||||
" for j in range(N):\n",
|
||||
" s.Add(sum(x[i][j] for i in range(M)) == float(bedarf[j]))\n",
|
||||
" for j in range(n):\n",
|
||||
" s.Add(sum(x[i][j] for i in range(m)) == float(bedarf[j]))\n",
|
||||
" s.Minimize(sum(float(kosten[i, j]) * x[i][j]\n",
|
||||
" for i in range(M) for j in range(N)))\n",
|
||||
" for i in range(m) for j in range(n)))\n",
|
||||
" aufbau = time.perf_counter() - t0\n",
|
||||
" t0 = time.perf_counter(); s.Solve(); loesen = time.perf_counter() - t0\n",
|
||||
" ausgabe = (s.Objective().Value(), aufbau, loesen, speicher_mb())\n",
|
||||
" \"\"\",\n",
|
||||
" return s.Objective().Value(), aufbau, loesen, speicher_mb()\n",
|
||||
"\n",
|
||||
" \"cvxpy\": \"\"\"\n",
|
||||
"\n",
|
||||
"def messe_cvxpy(m: int, n: int):\n",
|
||||
" import cvxpy as cp\n",
|
||||
" kosten, angebot, bedarf = instanz(m, n)\n",
|
||||
" t0 = time.perf_counter()\n",
|
||||
" x = cp.Variable((M, N), nonneg=True)\n",
|
||||
" x = cp.Variable((m, n), nonneg=True)\n",
|
||||
" problem = cp.Problem(cp.Minimize(cp.sum(cp.multiply(kosten, x))),\n",
|
||||
" [cp.sum(x, axis=1) <= angebot,\n",
|
||||
" cp.sum(x, axis=0) == bedarf])\n",
|
||||
" aufbau = time.perf_counter() - t0\n",
|
||||
" t0 = time.perf_counter(); problem.solve(); loesen = time.perf_counter() - t0\n",
|
||||
" ausgabe = (float(problem.value), aufbau, loesen, speicher_mb())\n",
|
||||
" \"\"\",\n",
|
||||
"}\n",
|
||||
" return float(problem.value), aufbau, loesen, speicher_mb()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def messe(name: str, quelltext: str, m: int, n: int):\n",
|
||||
" \"\"\"Fuehrt einen Ansatz in einem eigenen Prozess aus.\"\"\"\n",
|
||||
" programm = (VORSPANN.format(m=m, n=n) + textwrap.dedent(quelltext)\n",
|
||||
" + \"\\nprint(json.dumps(ausgabe))\\n\")\n",
|
||||
" ergebnis = subprocess.run([sys.executable, \"-c\", programm],\n",
|
||||
" capture_output=True, text=True, timeout=600)\n",
|
||||
" if ergebnis.returncode != 0:\n",
|
||||
" return None, ergebnis.stderr.strip().splitlines()[-1][:60]\n",
|
||||
" return json.loads(ergebnis.stdout.strip().splitlines()[-1]), None\n",
|
||||
"ANSAETZE = {\"scipy.linprog\": messe_scipy, \"highspy\": messe_highspy,\n",
|
||||
" \"ortools/GLOP\": messe_ortools, \"cvxpy\": messe_cvxpy}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def messe(funktion, m: int, n: int):\n",
|
||||
" \"\"\"Fuehrt eine Messfunktion in einem FRISCHEN Prozess aus.\n",
|
||||
"\n",
|
||||
" 'spawn' und max_tasks_per_child=1 zusammen garantieren, was Regel 1\n",
|
||||
" verlangt: Jede Messung sieht einen leeren Interpreter. Ohne das\n",
|
||||
" zweite wuerde der Pool seinen Arbeiter wiederverwenden - dann waere\n",
|
||||
" der Speicherwert der zweiten Bibliothek um die erste zu hoch, und\n",
|
||||
" ortools und highspy saessen im selben Prozess.\n",
|
||||
" \"\"\"\n",
|
||||
" with ProcessPoolExecutor(\n",
|
||||
" max_workers=1,\n",
|
||||
" mp_context=multiprocessing.get_context(\"spawn\"),\n",
|
||||
" max_tasks_per_child=1) as pool:\n",
|
||||
" try:\n",
|
||||
" return pool.submit(funktion, m, n).result(timeout=600), None\n",
|
||||
" except Exception as fehler:\n",
|
||||
" return None, str(fehler).strip().splitlines()[-1][:60]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
|
|
@ -717,8 +728,8 @@
|
|||
" f\"{'Loesen':>9} {'Anteil':>8} {'Speicher':>10}\")\n",
|
||||
" print(\" \" + \"-\" * 72)\n",
|
||||
" zielwerte = {}\n",
|
||||
" for name, quelltext in ANSAETZE.items():\n",
|
||||
" werte, fehler = messe(name, quelltext, m, n)\n",
|
||||
" for name, funktion in ANSAETZE.items():\n",
|
||||
" werte, fehler = messe(funktion, m, n)\n",
|
||||
" if werte is None:\n",
|
||||
" print(f\" {name:<16} nicht verfuegbar: {fehler}\")\n",
|
||||
" continue\n",
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -731,7 +731,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
|
|||
<h2 id="c9-importfehler">C9 — Importfehler</h2>
|
||||
<pre><code>ImportError: .../highspy/_core...so: undefined symbol: _ZN5Highs13releaseMemoryEv</code></pre>
|
||||
<p><strong>Ursache.</strong> <code>ortools</code> und <code>highspy</code> bringen beide eine eigene HiGHS-Kopie mit; sie lassen sich auf vielen Systemen <strong>nicht im selben Prozess</strong> importieren (siehe <a href="oekosystem.html#sec:oekosystem-ein-system-vier-programmieransaetze">Abschnitt 3.5</a>). Der Konflikt entsteht auch <strong>indirekt</strong>: <code>cvxpy</code> importiert ein installiertes <code>highspy</code> bei der Solver-Erkennung selbst mit — ein Skript, das erst <code>cvxpy</code> und dann <code>ortools</code> importiert, crasht daher mit derselben Meldung.</p>
|
||||
<p><strong>Abhilfen (in dieser Reihenfolge):</strong> 1. Nur eines von beiden im selben Skript verwenden. 2. Getrennte Prozesse (<code>subprocess</code>) — siehe <code>Ein_System_Vier_Ansaetze.py</code>. 3. Auf <code>highspy</code> verzichten: HiGHS ist ohnehin Backend von <code>scipy.optimize.linprog</code> und CVXPY. 4. Getrennte virtuelle Umgebungen.</p>
|
||||
<p><strong>Abhilfen (in dieser Reihenfolge):</strong> 1. Nur eines von beiden im selben Skript verwenden. 2. Getrennte Prozesse — ein <code>ProcessPoolExecutor</code> mit <code>mp_context="spawn"</code> und <code>max_tasks_per_child=1</code>, siehe <code>Ein_System_Vier_Ansaetze.py</code>. 3. Auf <code>highspy</code> verzichten: HiGHS ist ohnehin Backend von <code>scipy.optimize.linprog</code> und CVXPY. 4. Getrennte virtuelle Umgebungen.</p>
|
||||
<hr />
|
||||
<h2 id="c10-verdächtig-guter-backtest">C10 — Verdächtig guter Backtest</h2>
|
||||
<p><strong>Faustregel:</strong> Eine Sharpe Ratio über 2 bei einer einfachen Strategie ist fast immer ein Fehler, kein Fund.</p>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -523,6 +523,7 @@
|
|||
</ul></li>
|
||||
<li><a href="#sec:oekosystem-ein-system-vier-programmieransaetze" id="toc-sec:oekosystem-ein-system-vier-programmieransaetze">3.5 Ein System — vier Programmieransätze</a>
|
||||
<ul>
|
||||
<li><a href="#wie-die-isolation-aussieht-wenn-sie-tragen-soll" id="toc-wie-die-isolation-aussieht-wenn-sie-tragen-soll">Wie die Isolation aussieht, wenn sie tragen soll</a></li>
|
||||
<li><a href="#was-das-csr-format-bedeutet" id="toc-was-das-csr-format-bedeutet">Was das CSR-Format bedeutet</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#sec:oekosystem-wann-lohnt-sich-welche-ebene" id="toc-sec:oekosystem-wann-lohnt-sich-welche-ebene">3.6 Wann lohnt sich welche Ebene?</a></li>
|
||||
|
|
@ -5892,6 +5893,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="#kap-praxisfallen">Kapitel 22</a>) und <code>Benchmark_Skalierung.py</code> (<a href="#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="#sec:praxisfallen-or-kern">Abschnitt 22.6</a>).</p>
|
||||
<div class="sourceCode" id="cb34"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
|
||||
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Ein_System_Vier_Ansaetze.py</span></span>
|
||||
|
|
@ -5902,132 +5932,157 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg</code></pre>
|
|||
<span id="cb34-8"><a href="#cb34-8" aria-hidden="true" tabindex="-1"></a><span class="co"> 2*x1 + 3*x2 + x3 <= 50</span></span>
|
||||
<span id="cb34-9"><a href="#cb34-9" aria-hidden="true" tabindex="-1"></a><span class="co"> x >= 0</span></span>
|
||||
<span id="cb34-10"><a href="#cb34-10" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-11"><a href="#cb34-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="cb34-12"><a href="#cb34-12" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-13"><a href="#cb34-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="cb34-14"><a href="#cb34-14" aria-hidden="true" tabindex="-1"></a><span class="co">highspy auf vielen Systemen nicht gemeinsam importieren lassen (beide bringen</span></span>
|
||||
<span id="cb34-15"><a href="#cb34-15" aria-hidden="true" tabindex="-1"></a><span class="co">eine eigene HiGHS-Kopie mit -> Symbolkonflikt).</span></span>
|
||||
<span id="cb34-16"><a href="#cb34-16" aria-hidden="true" tabindex="-1"></a><span class="co">"""</span></span>
|
||||
<span id="cb34-11"><a href="#cb34-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="cb34-12"><a href="#cb34-12" aria-hidden="true" tabindex="-1"></a><span class="co">am Ende.</span></span>
|
||||
<span id="cb34-13"><a href="#cb34-13" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-14"><a href="#cb34-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="cb34-15"><a href="#cb34-15" aria-hidden="true" tabindex="-1"></a><span class="co">highspy auf vielen Systemen nicht gemeinsam importieren lassen (beide bringen</span></span>
|
||||
<span id="cb34-16"><a href="#cb34-16" aria-hidden="true" tabindex="-1"></a><span class="co">eine eigene HiGHS-Kopie mit -> Symbolkonflikt).</span></span>
|
||||
<span id="cb34-17"><a href="#cb34-17" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-18"><a href="#cb34-18" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> json</span>
|
||||
<span id="cb34-19"><a href="#cb34-19" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> subprocess</span>
|
||||
<span id="cb34-20"><a href="#cb34-20" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
|
||||
<span id="cb34-21"><a href="#cb34-21" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> textwrap</span>
|
||||
<span id="cb34-22"><a href="#cb34-22" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
|
||||
<span id="cb34-23"><a href="#cb34-23" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-24"><a href="#cb34-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="cb34-25"><a href="#cb34-25" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-26"><a href="#cb34-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="cb34-27"><a href="#cb34-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="cb34-28"><a href="#cb34-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="cb34-29"><a href="#cb34-29" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-30"><a href="#cb34-30" aria-hidden="true" tabindex="-1"></a> <span class="st">"scipy.optimize.linprog"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb34-31"><a href="#cb34-31" aria-hidden="true" tabindex="-1"></a><span class="st"> from scipy.optimize import linprog</span></span>
|
||||
<span id="cb34-32"><a href="#cb34-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="cb34-33"><a href="#cb34-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="cb34-34"><a href="#cb34-34" aria-hidden="true" tabindex="-1"></a><span class="st"> bounds=[(0, None)] * 3, method="highs")</span></span>
|
||||
<span id="cb34-35"><a href="#cb34-35" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (-res.fun, list(res.x))</span></span>
|
||||
<span id="cb34-36"><a href="#cb34-36" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb34-18"><a href="#cb34-18" aria-hidden="true" tabindex="-1"></a><span class="co">Die Isolation besorgt ein ProcessPoolExecutor. Drei Einstellungen ergeben</span></span>
|
||||
<span id="cb34-19"><a href="#cb34-19" aria-hidden="true" tabindex="-1"></a><span class="co">zusammen die Garantie:</span></span>
|
||||
<span id="cb34-20"><a href="#cb34-20" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-21"><a href="#cb34-21" aria-hidden="true" tabindex="-1"></a><span class="co"> mp_context "spawn" Der Kindprozess startet mit einem FRISCHEN</span></span>
|
||||
<span id="cb34-22"><a href="#cb34-22" aria-hidden="true" tabindex="-1"></a><span class="co"> Interpreter, statt den Speicher des Elternprozesses</span></span>
|
||||
<span id="cb34-23"><a href="#cb34-23" aria-hidden="true" tabindex="-1"></a><span class="co"> zu erben. Was hier schon importiert ist, ist dort</span></span>
|
||||
<span id="cb34-24"><a href="#cb34-24" aria-hidden="true" tabindex="-1"></a><span class="co"> nicht importiert. Mit dem Standard "fork" auf Linux</span></span>
|
||||
<span id="cb34-25"><a href="#cb34-25" aria-hidden="true" tabindex="-1"></a><span class="co"> waere das nicht so.</span></span>
|
||||
<span id="cb34-26"><a href="#cb34-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="cb34-27"><a href="#cb34-27" aria-hidden="true" tabindex="-1"></a><span class="co"> wuerde der Pool seinen Arbeiter wiederverwenden - und</span></span>
|
||||
<span id="cb34-28"><a href="#cb34-28" aria-hidden="true" tabindex="-1"></a><span class="co"> beim zweiten Solver waere der Konflikt zurueck.</span></span>
|
||||
<span id="cb34-29"><a href="#cb34-29" aria-hidden="true" tabindex="-1"></a><span class="co"> max_workers=1 Haelt die vier Laeufe nacheinander. Nicht aus</span></span>
|
||||
<span id="cb34-30"><a href="#cb34-30" aria-hidden="true" tabindex="-1"></a><span class="co"> Vorsicht, sondern damit die gemessenen Zeiten</span></span>
|
||||
<span id="cb34-31"><a href="#cb34-31" aria-hidden="true" tabindex="-1"></a><span class="co"> vergleichbar bleiben.</span></span>
|
||||
<span id="cb34-32"><a href="#cb34-32" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-33"><a href="#cb34-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="cb34-34"><a href="#cb34-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="cb34-35"><a href="#cb34-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="cb34-36"><a href="#cb34-36" aria-hidden="true" tabindex="-1"></a><span class="co">String nicht.</span></span>
|
||||
<span id="cb34-37"><a href="#cb34-37" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-38"><a href="#cb34-38" aria-hidden="true" tabindex="-1"></a> <span class="st">"highspy (natives HiGHS)"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb34-39"><a href="#cb34-39" aria-hidden="true" tabindex="-1"></a><span class="st"> import numpy as np, highspy</span></span>
|
||||
<span id="cb34-40"><a href="#cb34-40" aria-hidden="true" tabindex="-1"></a><span class="st"> h = highspy.Highs()</span></span>
|
||||
<span id="cb34-41"><a href="#cb34-41" aria-hidden="true" tabindex="-1"></a><span class="st"> h.setOptionValue("output_flag", False)</span></span>
|
||||
<span id="cb34-42"><a href="#cb34-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="cb34-43"><a href="#cb34-43" aria-hidden="true" tabindex="-1"></a><span class="st"> h.changeObjectiveSense(highspy.ObjSense.kMaximize)</span></span>
|
||||
<span id="cb34-44"><a href="#cb34-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="cb34-45"><a href="#cb34-45" aria-hidden="true" tabindex="-1"></a><span class="st"> h.changeColCost(j, wert)</span></span>
|
||||
<span id="cb34-46"><a href="#cb34-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="cb34-47"><a href="#cb34-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="cb34-48"><a href="#cb34-48" aria-hidden="true" tabindex="-1"></a><span class="st"> np.array([0, 3], dtype=np.int32),</span></span>
|
||||
<span id="cb34-49"><a href="#cb34-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="cb34-50"><a href="#cb34-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="cb34-51"><a href="#cb34-51" aria-hidden="true" tabindex="-1"></a><span class="st"> h.run()</span></span>
|
||||
<span id="cb34-52"><a href="#cb34-52" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (h.getInfo().objective_function_value,</span></span>
|
||||
<span id="cb34-53"><a href="#cb34-53" aria-hidden="true" tabindex="-1"></a><span class="st"> list(h.getSolution().col_value[:3]))</span></span>
|
||||
<span id="cb34-54"><a href="#cb34-54" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb34-55"><a href="#cb34-55" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-56"><a href="#cb34-56" aria-hidden="true" tabindex="-1"></a> <span class="st">"cvxpy"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb34-57"><a href="#cb34-57" aria-hidden="true" tabindex="-1"></a><span class="st"> import numpy as np, cvxpy as cp</span></span>
|
||||
<span id="cb34-58"><a href="#cb34-58" aria-hidden="true" tabindex="-1"></a><span class="st"> x = cp.Variable(3, nonneg=True)</span></span>
|
||||
<span id="cb34-59"><a href="#cb34-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="cb34-60"><a href="#cb34-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="cb34-61"><a href="#cb34-61" aria-hidden="true" tabindex="-1"></a><span class="st"> problem.solve()</span></span>
|
||||
<span id="cb34-62"><a href="#cb34-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="cb34-63"><a href="#cb34-63" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb34-64"><a href="#cb34-64" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-65"><a href="#cb34-65" aria-hidden="true" tabindex="-1"></a> <span class="st">"ortools / GLOP"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb34-66"><a href="#cb34-66" aria-hidden="true" tabindex="-1"></a><span class="st"> from ortools.linear_solver import pywraplp</span></span>
|
||||
<span id="cb34-67"><a href="#cb34-67" aria-hidden="true" tabindex="-1"></a><span class="st"> s = pywraplp.Solver.CreateSolver("GLOP")</span></span>
|
||||
<span id="cb34-68"><a href="#cb34-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="cb34-69"><a href="#cb34-69" aria-hidden="true" tabindex="-1"></a><span class="st"> A = [[1, 1, 2], [2, 3, 1]]</span></span>
|
||||
<span id="cb34-70"><a href="#cb34-70" aria-hidden="true" tabindex="-1"></a><span class="st"> for i, kap in enumerate([40, 50]):</span></span>
|
||||
<span id="cb34-71"><a href="#cb34-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="cb34-72"><a href="#cb34-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="cb34-73"><a href="#cb34-73" aria-hidden="true" tabindex="-1"></a><span class="st"> s.Solve()</span></span>
|
||||
<span id="cb34-74"><a href="#cb34-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="cb34-75"><a href="#cb34-75" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb34-76"><a href="#cb34-76" aria-hidden="true" tabindex="-1"></a>}</span>
|
||||
<span id="cb34-77"><a href="#cb34-77" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-38"><a href="#cb34-38" aria-hidden="true" tabindex="-1"></a><span class="co">Benoetigt: scipy, highspy, cvxpy, ortools</span></span>
|
||||
<span id="cb34-39"><a href="#cb34-39" aria-hidden="true" tabindex="-1"></a><span class="co">"""</span></span>
|
||||
<span id="cb34-40"><a href="#cb34-40" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-41"><a href="#cb34-41" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> multiprocessing</span>
|
||||
<span id="cb34-42"><a href="#cb34-42" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
|
||||
<span id="cb34-43"><a href="#cb34-43" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> concurrent.futures <span class="im">import</span> ProcessPoolExecutor</span>
|
||||
<span id="cb34-44"><a href="#cb34-44" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-45"><a href="#cb34-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="cb34-46"><a href="#cb34-46" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-47"><a href="#cb34-47" aria-hidden="true" tabindex="-1"></a><span class="co"># Die Instanz - einmal notiert, von allen vier Funktionen benutzt.</span></span>
|
||||
<span id="cb34-48"><a href="#cb34-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="cb34-49"><a href="#cb34-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="cb34-50"><a href="#cb34-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="cb34-51"><a href="#cb34-51" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-52"><a href="#cb34-52" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-53"><a href="#cb34-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="cb34-54"><a href="#cb34-54" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> scipy.optimize <span class="im">import</span> linprog</span>
|
||||
<span id="cb34-55"><a href="#cb34-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="cb34-56"><a href="#cb34-56" aria-hidden="true" tabindex="-1"></a> A_ub<span class="op">=</span>MATRIX, b_ub<span class="op">=</span>KAPAZITAET,</span>
|
||||
<span id="cb34-57"><a href="#cb34-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="cb34-58"><a href="#cb34-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="cb34-59"><a href="#cb34-59" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-60"><a href="#cb34-60" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-61"><a href="#cb34-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="cb34-62"><a href="#cb34-62" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> highspy</span>
|
||||
<span id="cb34-63"><a href="#cb34-63" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> numpy <span class="im">as</span> np</span>
|
||||
<span id="cb34-64"><a href="#cb34-64" aria-hidden="true" tabindex="-1"></a> h <span class="op">=</span> highspy.Highs()</span>
|
||||
<span id="cb34-65"><a href="#cb34-65" aria-hidden="true" tabindex="-1"></a> h.setOptionValue(<span class="st">"output_flag"</span>, <span class="va">False</span>)</span>
|
||||
<span id="cb34-66"><a href="#cb34-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="cb34-67"><a href="#cb34-67" aria-hidden="true" tabindex="-1"></a> h.changeObjectiveSense(highspy.ObjSense.kMaximize)</span>
|
||||
<span id="cb34-68"><a href="#cb34-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="cb34-69"><a href="#cb34-69" aria-hidden="true" tabindex="-1"></a> h.changeColCost(j, wert)</span>
|
||||
<span id="cb34-70"><a href="#cb34-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="cb34-71"><a href="#cb34-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="cb34-72"><a href="#cb34-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="cb34-73"><a href="#cb34-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="cb34-74"><a href="#cb34-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="cb34-75"><a href="#cb34-75" aria-hidden="true" tabindex="-1"></a> h.run()</span>
|
||||
<span id="cb34-76"><a href="#cb34-76" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> (h.getInfo().objective_function_value,</span>
|
||||
<span id="cb34-77"><a href="#cb34-77" aria-hidden="true" tabindex="-1"></a> <span class="bu">list</span>(h.getSolution().col_value[:<span class="dv">3</span>]))</span>
|
||||
<span id="cb34-78"><a href="#cb34-78" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-79"><a href="#cb34-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="cb34-80"><a href="#cb34-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="cb34-81"><a href="#cb34-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="cb34-82"><a href="#cb34-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="cb34-83"><a href="#cb34-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="cb34-84"><a href="#cb34-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="cb34-85"><a href="#cb34-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="cb34-86"><a href="#cb34-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="cb34-87"><a href="#cb34-87" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> wert, loesung</span>
|
||||
<span id="cb34-79"><a href="#cb34-79" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-80"><a href="#cb34-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="cb34-81"><a href="#cb34-81" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> cvxpy <span class="im">as</span> cp</span>
|
||||
<span id="cb34-82"><a href="#cb34-82" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> numpy <span class="im">as</span> np</span>
|
||||
<span id="cb34-83"><a href="#cb34-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="cb34-84"><a href="#cb34-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="cb34-85"><a href="#cb34-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="cb34-86"><a href="#cb34-86" aria-hidden="true" tabindex="-1"></a> problem.solve()</span>
|
||||
<span id="cb34-87"><a href="#cb34-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="cb34-88"><a href="#cb34-88" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-89"><a href="#cb34-89" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-90"><a href="#cb34-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="cb34-91"><a href="#cb34-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="cb34-92"><a href="#cb34-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="cb34-93"><a href="#cb34-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="cb34-94"><a href="#cb34-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="cb34-95"><a href="#cb34-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="cb34-96"><a href="#cb34-96" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-97"><a href="#cb34-97" aria-hidden="true" tabindex="-1"></a> werte <span class="op">=</span> []</span>
|
||||
<span id="cb34-98"><a href="#cb34-98" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, quelltext <span class="kw">in</span> ANSAETZE.items():</span>
|
||||
<span id="cb34-99"><a href="#cb34-99" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb34-100"><a href="#cb34-100" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span>:</span>
|
||||
<span id="cb34-101"><a href="#cb34-101" aria-hidden="true" tabindex="-1"></a> wert, x <span class="op">=</span> fuehre_in_eigenem_prozess_aus(quelltext)</span>
|
||||
<span id="cb34-102"><a href="#cb34-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="cb34-103"><a href="#cb34-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="cb34-104"><a href="#cb34-104" aria-hidden="true" tabindex="-1"></a> <span class="cf">continue</span></span>
|
||||
<span id="cb34-105"><a href="#cb34-105" aria-hidden="true" tabindex="-1"></a> dauer <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb34-106"><a href="#cb34-106" aria-hidden="true" tabindex="-1"></a> werte.append(wert)</span>
|
||||
<span id="cb34-107"><a href="#cb34-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="cb34-108"><a href="#cb34-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="cb34-109"><a href="#cb34-109" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-110"><a href="#cb34-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="cb34-111"><a href="#cb34-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="cb34-112"><a href="#cb34-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="cb34-113"><a href="#cb34-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="cb34-114"><a href="#cb34-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="cb34-115"><a href="#cb34-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="cb34-116"><a href="#cb34-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="cb34-117"><a href="#cb34-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="cb34-118"><a href="#cb34-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="cb34-119"><a href="#cb34-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="cb34-120"><a href="#cb34-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="cb34-90"><a href="#cb34-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="cb34-91"><a href="#cb34-91" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> ortools.linear_solver <span class="im">import</span> pywraplp</span>
|
||||
<span id="cb34-92"><a href="#cb34-92" aria-hidden="true" tabindex="-1"></a> s <span class="op">=</span> pywraplp.Solver.CreateSolver(<span class="st">"GLOP"</span>)</span>
|
||||
<span id="cb34-93"><a href="#cb34-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="cb34-94"><a href="#cb34-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="cb34-95"><a href="#cb34-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="cb34-96"><a href="#cb34-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="cb34-97"><a href="#cb34-97" aria-hidden="true" tabindex="-1"></a> s.Solve()</span>
|
||||
<span id="cb34-98"><a href="#cb34-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="cb34-99"><a href="#cb34-99" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-100"><a href="#cb34-100" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-101"><a href="#cb34-101" aria-hidden="true" tabindex="-1"></a>ANSAETZE <span class="op">=</span> {</span>
|
||||
<span id="cb34-102"><a href="#cb34-102" aria-hidden="true" tabindex="-1"></a> <span class="st">"scipy.optimize.linprog"</span>: loese_mit_scipy,</span>
|
||||
<span id="cb34-103"><a href="#cb34-103" aria-hidden="true" tabindex="-1"></a> <span class="st">"highspy (natives HiGHS)"</span>: loese_mit_highspy,</span>
|
||||
<span id="cb34-104"><a href="#cb34-104" aria-hidden="true" tabindex="-1"></a> <span class="st">"cvxpy"</span>: loese_mit_cvxpy,</span>
|
||||
<span id="cb34-105"><a href="#cb34-105" aria-hidden="true" tabindex="-1"></a> <span class="st">"ortools / GLOP"</span>: loese_mit_ortools,</span>
|
||||
<span id="cb34-106"><a href="#cb34-106" aria-hidden="true" tabindex="-1"></a>}</span>
|
||||
<span id="cb34-107"><a href="#cb34-107" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-108"><a href="#cb34-108" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-109"><a href="#cb34-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="cb34-110"><a href="#cb34-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="cb34-111"><a href="#cb34-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="cb34-112"><a href="#cb34-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="cb34-113"><a href="#cb34-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="cb34-114"><a href="#cb34-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="cb34-115"><a href="#cb34-115" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-116"><a href="#cb34-116" aria-hidden="true" tabindex="-1"></a> werte <span class="op">=</span> []</span>
|
||||
<span id="cb34-117"><a href="#cb34-117" aria-hidden="true" tabindex="-1"></a> <span class="co"># Ein Pool, vier Aufgaben, vier frische Prozesse. Der Kontext muss</span></span>
|
||||
<span id="cb34-118"><a href="#cb34-118" aria-hidden="true" tabindex="-1"></a> <span class="co"># "spawn" sein - siehe Modulkommentar.</span></span>
|
||||
<span id="cb34-119"><a href="#cb34-119" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> ProcessPoolExecutor(</span>
|
||||
<span id="cb34-120"><a href="#cb34-120" aria-hidden="true" tabindex="-1"></a> max_workers<span class="op">=</span><span class="dv">1</span>,</span>
|
||||
<span id="cb34-121"><a href="#cb34-121" aria-hidden="true" tabindex="-1"></a> mp_context<span class="op">=</span>multiprocessing.get_context(<span class="st">"spawn"</span>),</span>
|
||||
<span id="cb34-122"><a href="#cb34-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="cb34-123"><a href="#cb34-123" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, funktion <span class="kw">in</span> ANSAETZE.items():</span>
|
||||
<span id="cb34-124"><a href="#cb34-124" aria-hidden="true" tabindex="-1"></a> beginn <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb34-125"><a href="#cb34-125" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span>:</span>
|
||||
<span id="cb34-126"><a href="#cb34-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="cb34-127"><a href="#cb34-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="cb34-128"><a href="#cb34-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="cb34-129"><a href="#cb34-129" aria-hidden="true" tabindex="-1"></a> <span class="cf">continue</span></span>
|
||||
<span id="cb34-130"><a href="#cb34-130" aria-hidden="true" tabindex="-1"></a> dauer <span class="op">=</span> time.perf_counter() <span class="op">-</span> beginn</span>
|
||||
<span id="cb34-131"><a href="#cb34-131" aria-hidden="true" tabindex="-1"></a> werte.append(wert)</span>
|
||||
<span id="cb34-132"><a href="#cb34-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="cb34-133"><a href="#cb34-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="cb34-134"><a href="#cb34-134" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb34-135"><a href="#cb34-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="cb34-136"><a href="#cb34-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="cb34-137"><a href="#cb34-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="cb34-138"><a href="#cb34-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="cb34-139"><a href="#cb34-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="cb34-140"><a href="#cb34-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="cb34-141"><a href="#cb34-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="cb34-142"><a href="#cb34-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="cb34-143"><a href="#cb34-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="cb34-144"><a href="#cb34-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="cb34-145"><a href="#cb34-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>
|
||||
|
|
@ -27339,9 +27394,9 @@ Domaenenschicht.
|
|||
<span id="cb213-36"><a href="#cb213-36" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-37"><a href="#cb213-37" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
|
||||
<span id="cb213-38"><a href="#cb213-38" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-39"><a href="#cb213-39" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> subprocess</span>
|
||||
<span id="cb213-40"><a href="#cb213-40" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
|
||||
<span id="cb213-41"><a href="#cb213-41" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
|
||||
<span id="cb213-39"><a href="#cb213-39" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> multiprocessing</span>
|
||||
<span id="cb213-40"><a href="#cb213-40" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
|
||||
<span id="cb213-41"><a href="#cb213-41" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> concurrent.futures <span class="im">import</span> ProcessPoolExecutor</span>
|
||||
<span id="cb213-42"><a href="#cb213-42" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-43"><a href="#cb213-43" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
|
||||
<span id="cb213-44"><a href="#cb213-44" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> pydantic <span class="im">import</span> BaseModel, Field, model_validator</span>
|
||||
|
|
@ -27544,83 +27599,87 @@ Domaenenschicht.
|
|||
<span id="cb213-241"><a href="#cb213-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">></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="cb213-242"><a href="#cb213-242" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-243"><a href="#cb213-243" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-244"><a href="#cb213-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">-></span> Loesung:</span>
|
||||
<span id="cb213-245"><a href="#cb213-245" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Startet dieses Programm noch einmal - mit genau einem Solverimport."""</span></span>
|
||||
<span id="cb213-246"><a href="#cb213-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="cb213-247"><a href="#cb213-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="cb213-248"><a href="#cb213-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="cb213-249"><a href="#cb213-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="cb213-250"><a href="#cb213-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="cb213-251"><a href="#cb213-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="cb213-244"><a href="#cb213-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">-></span> Loesung:</span>
|
||||
<span id="cb213-245"><a href="#cb213-245" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Laesst genau einen Modellbauer in einem frischen Prozess rechnen.</span></span>
|
||||
<span id="cb213-246"><a href="#cb213-246" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-247"><a href="#cb213-247" aria-hidden="true" tabindex="-1"></a><span class="co"> 'spawn' statt des Linux-Standards 'fork': Der Kindprozess startet mit</span></span>
|
||||
<span id="cb213-248"><a href="#cb213-248" aria-hidden="true" tabindex="-1"></a><span class="co"> einem leeren Interpreter und importiert nur den Solver, den SEIN</span></span>
|
||||
<span id="cb213-249"><a href="#cb213-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="cb213-250"><a href="#cb213-250" aria-hidden="true" tabindex="-1"></a><span class="co"> seinen Arbeiter nicht wiederverwendet - sonst saessen beim zweiten Aufruf</span></span>
|
||||
<span id="cb213-251"><a href="#cb213-251" aria-hidden="true" tabindex="-1"></a><span class="co"> wieder beide Bibliotheken im selben Prozess.</span></span>
|
||||
<span id="cb213-252"><a href="#cb213-252" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-253"><a href="#cb213-253" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-254"><a href="#cb213-254" 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="cb213-255"><a href="#cb213-255" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> beispielproblem()</span>
|
||||
<span id="cb213-256"><a href="#cb213-256" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-257"><a href="#cb213-257" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Kindprozess: rechnen und das DTO als JSON ausgeben ---------------</span></span>
|
||||
<span id="cb213-258"><a href="#cb213-258" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">len</span>(sys.argv) <span class="op">></span> <span class="dv">1</span>:</span>
|
||||
<span id="cb213-259"><a href="#cb213-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="cb213-260"><a href="#cb213-260" aria-hidden="true" tabindex="-1"></a> sys.exit(<span class="dv">0</span>)</span>
|
||||
<span id="cb213-253"><a href="#cb213-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="cb213-254"><a href="#cb213-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="cb213-255"><a href="#cb213-255" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
|
||||
<span id="cb213-256"><a href="#cb213-256" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> ProcessPoolExecutor(</span>
|
||||
<span id="cb213-257"><a href="#cb213-257" aria-hidden="true" tabindex="-1"></a> max_workers<span class="op">=</span><span class="dv">1</span>,</span>
|
||||
<span id="cb213-258"><a href="#cb213-258" aria-hidden="true" tabindex="-1"></a> mp_context<span class="op">=</span>multiprocessing.get_context(<span class="st">"spawn"</span>),</span>
|
||||
<span id="cb213-259"><a href="#cb213-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="cb213-260"><a href="#cb213-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="cb213-261"><a href="#cb213-261" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-262"><a href="#cb213-262" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Hauptprozess: beide Solver anstossen und vergleichen -------------</span></span>
|
||||
<span id="cb213-263"><a href="#cb213-263" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">82</span>)</span>
|
||||
<span id="cb213-264"><a href="#cb213-264" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE"</span>)</span>
|
||||
<span id="cb213-265"><a href="#cb213-265" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">82</span>)</span>
|
||||
<span id="cb213-266"><a href="#cb213-266" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Standortplanung: </span><span class="sc">{</span><span class="bu">len</span>(problem.lager)<span class="sc">}</span><span class="ss"> moegliche Lager, "</span></span>
|
||||
<span id="cb213-267"><a href="#cb213-267" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</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."</span>)</span>
|
||||
<span id="cb213-268"><a href="#cb213-268" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"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 "</span></span>
|
||||
<span id="cb213-269"><a href="#cb213-269" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"-> mindestens 3 Lager noetig.</span><span class="ch">\n</span><span class="ss">"</span>)</span>
|
||||
<span id="cb213-270"><a href="#cb213-270" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-271"><a href="#cb213-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="cb213-272"><a href="#cb213-272" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, beschriftung <span class="kw">in</span> [(<span class="st">"cpsat"</span>, <span class="st">"OR-Tools CP-SAT"</span>),</span>
|
||||
<span id="cb213-273"><a href="#cb213-273" aria-hidden="true" tabindex="-1"></a> (<span class="st">"highs"</span>, <span class="st">"HiGHS (highspy)"</span>)]:</span>
|
||||
<span id="cb213-274"><a href="#cb213-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="cb213-275"><a href="#cb213-275" aria-hidden="true" tabindex="-1"></a> beanstandungen <span class="op">=</span> pruefe_zuordnung(problem, loesung)</span>
|
||||
<span id="cb213-276"><a href="#cb213-276" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-277"><a href="#cb213-277" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span>beschriftung<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb213-278"><a href="#cb213-278" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>loesung<span class="sc">.</span>als_bericht()<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb213-279"><a href="#cb213-279" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" eroeffnete Lager: </span><span class="sc">{</span><span class="st">', '</span><span class="sc">.</span>join(geoeffnete_lager(problem, loesung))<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb213-280"><a href="#cb213-280" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Abnahmepruefung: "</span></span>
|
||||
<span id="cb213-281"><a href="#cb213-281" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'bestanden'</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">"</span>)</span>
|
||||
<span id="cb213-282"><a href="#cb213-282" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-283"><a href="#cb213-283" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Was der Vergleich zeigt -----------------------------------------</span></span>
|
||||
<span id="cb213-284"><a href="#cb213-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="cb213-285"><a href="#cb213-285" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"-"</span> <span class="op">*</span> <span class="dv">82</span>)</span>
|
||||
<span id="cb213-286"><a href="#cb213-286" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"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"</span>)</span>
|
||||
<span id="cb213-287"><a href="#cb213-287" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-288"><a href="#cb213-288" aria-hidden="true" tabindex="-1"></a> gleich_belegt <span class="op">=</span> <span class="bu">all</span>(</span>
|
||||
<span id="cb213-289"><a href="#cb213-289" aria-hidden="true" tabindex="-1"></a> <span class="bu">round</span>(loesungen[<span class="st">"cpsat"</span>].werte[s]) <span class="op">==</span> <span class="bu">round</span>(loesungen[<span class="st">"highs"</span>].werte[s])</span>
|
||||
<span id="cb213-290"><a href="#cb213-290" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> s <span class="kw">in</span> loesungen[<span class="st">"cpsat"</span>].werte)</span>
|
||||
<span id="cb213-291"><a href="#cb213-291" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Identische Zuordnung: </span><span class="sc">{</span><span class="st">'ja'</span> <span class="cf">if</span> gleich_belegt <span class="cf">else</span> <span class="st">'nein'</span><span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb213-292"><a href="#cb213-292" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-293"><a href="#cb213-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"><</span> <span class="fl">0.5</span>, <span class="st">"Die Solver widersprechen sich!"</span></span>
|
||||
<span id="cb213-294"><a href="#cb213-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="cb213-295"><a href="#cb213-295" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-296"><a href="#cb213-296" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"="</span> <span class="op">*</span> <span class="dv">82</span>)</span>
|
||||
<span id="cb213-297"><a href="#cb213-297" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" WAS DER WECHSEL GEKOSTET HAT"</span>)</span>
|
||||
<span id="cb213-298"><a href="#cb213-298" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">82</span>)</span>
|
||||
<span id="cb213-299"><a href="#cb213-299" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Ausgetauscht wurde EINE Funktion. Domaenenmodell, Abnahmepruefung und"</span>)</span>
|
||||
<span id="cb213-300"><a href="#cb213-300" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Bericht sind woertlich dieselben - sie sehen den Solver nie."</span>)</span>
|
||||
<span id="cb213-301"><a href="#cb213-301" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb213-302"><a href="#cb213-302" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Nicht umsonst ist der Wechsel trotzdem:"</span>)</span>
|
||||
<span id="cb213-303"><a href="#cb213-303" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * CP-SAT rechnet ausschliesslich GANZZAHLIG. Alle Kosten sind hier"</span>)</span>
|
||||
<span id="cb213-304"><a href="#cb213-304" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" deshalb int. Wer in Euro und Cent rechnet, skaliert vorher auf Cent -"</span>)</span>
|
||||
<span id="cb213-305"><a href="#cb213-305" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" und muss das im Bericht wieder zuruecknehmen."</span>)</span>
|
||||
<span id="cb213-306"><a href="#cb213-306" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * HiGHS braucht die Restriktionen als Matrixzeilen, CP-SAT nimmt sie"</span>)</span>
|
||||
<span id="cb213-307"><a href="#cb213-307" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" als Ausdruecke. Das ist der Grund, warum der HiGHS-Modellbauer"</span>)</span>
|
||||
<span id="cb213-308"><a href="#cb213-308" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" laenger ist, obwohl er dasselbe Modell beschreibt."</span>)</span>
|
||||
<span id="cb213-309"><a href="#cb213-309" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Beide Bibliotheken bringen eine eigene HiGHS-Kopie mit und lassen"</span>)</span>
|
||||
<span id="cb213-310"><a href="#cb213-310" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" sich nicht gemeinsam importieren - daher die zwei Prozesse."</span>)</span>
|
||||
<span id="cb213-311"><a href="#cb213-311" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb213-312"><a href="#cb213-312" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Der Ertrag: Beide beweisen denselben optimalen Zielwert, und die"</span>)</span>
|
||||
<span id="cb213-313"><a href="#cb213-313" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Entscheidung zwischen ihnen ist eine Frage der Laufzeit geworden -"</span>)</span>
|
||||
<span id="cb213-314"><a href="#cb213-314" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"nicht eine Frage, wie viel Code man neu schreiben muss."</span>)</span>
|
||||
<span id="cb213-262"><a href="#cb213-262" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-263"><a href="#cb213-263" 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="cb213-264"><a href="#cb213-264" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> beispielproblem()</span>
|
||||
<span id="cb213-265"><a href="#cb213-265" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-266"><a href="#cb213-266" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Beide Solver anstossen und vergleichen ---------------------------</span></span>
|
||||
<span id="cb213-267"><a href="#cb213-267" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">82</span>)</span>
|
||||
<span id="cb213-268"><a href="#cb213-268" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE"</span>)</span>
|
||||
<span id="cb213-269"><a href="#cb213-269" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">82</span>)</span>
|
||||
<span id="cb213-270"><a href="#cb213-270" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Standortplanung: </span><span class="sc">{</span><span class="bu">len</span>(problem.lager)<span class="sc">}</span><span class="ss"> moegliche Lager, "</span></span>
|
||||
<span id="cb213-271"><a href="#cb213-271" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</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."</span>)</span>
|
||||
<span id="cb213-272"><a href="#cb213-272" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"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 "</span></span>
|
||||
<span id="cb213-273"><a href="#cb213-273" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"-> mindestens 3 Lager noetig.</span><span class="ch">\n</span><span class="ss">"</span>)</span>
|
||||
<span id="cb213-274"><a href="#cb213-274" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-275"><a href="#cb213-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="cb213-276"><a href="#cb213-276" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, beschriftung <span class="kw">in</span> [(<span class="st">"cpsat"</span>, <span class="st">"OR-Tools CP-SAT"</span>),</span>
|
||||
<span id="cb213-277"><a href="#cb213-277" aria-hidden="true" tabindex="-1"></a> (<span class="st">"highs"</span>, <span class="st">"HiGHS (highspy)"</span>)]:</span>
|
||||
<span id="cb213-278"><a href="#cb213-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="cb213-279"><a href="#cb213-279" aria-hidden="true" tabindex="-1"></a> beanstandungen <span class="op">=</span> pruefe_zuordnung(problem, loesung)</span>
|
||||
<span id="cb213-280"><a href="#cb213-280" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-281"><a href="#cb213-281" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span>beschriftung<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb213-282"><a href="#cb213-282" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>loesung<span class="sc">.</span>als_bericht()<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb213-283"><a href="#cb213-283" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" eroeffnete Lager: </span><span class="sc">{</span><span class="st">', '</span><span class="sc">.</span>join(geoeffnete_lager(problem, loesung))<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb213-284"><a href="#cb213-284" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Abnahmepruefung: "</span></span>
|
||||
<span id="cb213-285"><a href="#cb213-285" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'bestanden'</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">"</span>)</span>
|
||||
<span id="cb213-286"><a href="#cb213-286" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-287"><a href="#cb213-287" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Was der Vergleich zeigt -----------------------------------------</span></span>
|
||||
<span id="cb213-288"><a href="#cb213-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="cb213-289"><a href="#cb213-289" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"-"</span> <span class="op">*</span> <span class="dv">82</span>)</span>
|
||||
<span id="cb213-290"><a href="#cb213-290" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"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"</span>)</span>
|
||||
<span id="cb213-291"><a href="#cb213-291" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-292"><a href="#cb213-292" aria-hidden="true" tabindex="-1"></a> gleich_belegt <span class="op">=</span> <span class="bu">all</span>(</span>
|
||||
<span id="cb213-293"><a href="#cb213-293" aria-hidden="true" tabindex="-1"></a> <span class="bu">round</span>(loesungen[<span class="st">"cpsat"</span>].werte[s]) <span class="op">==</span> <span class="bu">round</span>(loesungen[<span class="st">"highs"</span>].werte[s])</span>
|
||||
<span id="cb213-294"><a href="#cb213-294" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> s <span class="kw">in</span> loesungen[<span class="st">"cpsat"</span>].werte)</span>
|
||||
<span id="cb213-295"><a href="#cb213-295" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Identische Zuordnung: </span><span class="sc">{</span><span class="st">'ja'</span> <span class="cf">if</span> gleich_belegt <span class="cf">else</span> <span class="st">'nein'</span><span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb213-296"><a href="#cb213-296" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-297"><a href="#cb213-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"><</span> <span class="fl">0.5</span>, <span class="st">"Die Solver widersprechen sich!"</span></span>
|
||||
<span id="cb213-298"><a href="#cb213-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="cb213-299"><a href="#cb213-299" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb213-300"><a href="#cb213-300" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"="</span> <span class="op">*</span> <span class="dv">82</span>)</span>
|
||||
<span id="cb213-301"><a href="#cb213-301" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" WAS DER WECHSEL GEKOSTET HAT"</span>)</span>
|
||||
<span id="cb213-302"><a href="#cb213-302" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">82</span>)</span>
|
||||
<span id="cb213-303"><a href="#cb213-303" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Ausgetauscht wurde EINE Funktion. Domaenenmodell, Abnahmepruefung und"</span>)</span>
|
||||
<span id="cb213-304"><a href="#cb213-304" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Bericht sind woertlich dieselben - sie sehen den Solver nie."</span>)</span>
|
||||
<span id="cb213-305"><a href="#cb213-305" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb213-306"><a href="#cb213-306" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Nicht umsonst ist der Wechsel trotzdem:"</span>)</span>
|
||||
<span id="cb213-307"><a href="#cb213-307" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * CP-SAT rechnet ausschliesslich GANZZAHLIG. Alle Kosten sind hier"</span>)</span>
|
||||
<span id="cb213-308"><a href="#cb213-308" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" deshalb int. Wer in Euro und Cent rechnet, skaliert vorher auf Cent -"</span>)</span>
|
||||
<span id="cb213-309"><a href="#cb213-309" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" und muss das im Bericht wieder zuruecknehmen."</span>)</span>
|
||||
<span id="cb213-310"><a href="#cb213-310" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * HiGHS braucht die Restriktionen als Matrixzeilen, CP-SAT nimmt sie"</span>)</span>
|
||||
<span id="cb213-311"><a href="#cb213-311" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" als Ausdruecke. Das ist der Grund, warum der HiGHS-Modellbauer"</span>)</span>
|
||||
<span id="cb213-312"><a href="#cb213-312" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" laenger ist, obwohl er dasselbe Modell beschreibt."</span>)</span>
|
||||
<span id="cb213-313"><a href="#cb213-313" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Beide Bibliotheken bringen eine eigene HiGHS-Kopie mit und lassen"</span>)</span>
|
||||
<span id="cb213-314"><a href="#cb213-314" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" sich nicht gemeinsam importieren - daher die zwei Prozesse."</span>)</span>
|
||||
<span id="cb213-315"><a href="#cb213-315" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb213-316"><a href="#cb213-316" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Verglichen wird deshalb der ZIELWERT, nicht der Plan: Gibt es mehrere"</span>)</span>
|
||||
<span id="cb213-317"><a href="#cb213-317" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"gleich teure Loesungen, darf jeder Solver eine andere davon liefern."</span>)</span>
|
||||
<span id="cb213-318"><a href="#cb213-318" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Hier stimmen sie zufaellig ueberein - darauf zu testen waere trotzdem"</span>)</span>
|
||||
<span id="cb213-319"><a href="#cb213-319" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"ein unzuverlaessiger Test (siehe JobShop_Intervalle.py)."</span>)</span>
|
||||
<span id="cb213-320"><a href="#cb213-320" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">82</span>)</span></code></pre></div>
|
||||
<span id="cb213-316"><a href="#cb213-316" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Der Ertrag: Beide beweisen denselben optimalen Zielwert, und die"</span>)</span>
|
||||
<span id="cb213-317"><a href="#cb213-317" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Entscheidung zwischen ihnen ist eine Frage der Laufzeit geworden -"</span>)</span>
|
||||
<span id="cb213-318"><a href="#cb213-318" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"nicht eine Frage, wie viel Code man neu schreiben muss."</span>)</span>
|
||||
<span id="cb213-319"><a href="#cb213-319" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb213-320"><a href="#cb213-320" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Verglichen wird deshalb der ZIELWERT, nicht der Plan: Gibt es mehrere"</span>)</span>
|
||||
<span id="cb213-321"><a href="#cb213-321" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"gleich teure Loesungen, darf jeder Solver eine andere davon liefern."</span>)</span>
|
||||
<span id="cb213-322"><a href="#cb213-322" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Hier stimmen sie zufaellig ueberein - darauf zu testen waere trotzdem"</span>)</span>
|
||||
<span id="cb213-323"><a href="#cb213-323" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"ein unzuverlaessiger Test (siehe JobShop_Intervalle.py)."</span>)</span>
|
||||
<span id="cb213-324"><a href="#cb213-324" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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
|
||||
|
|
@ -28834,173 +28893,184 @@ moeglichen Fehler. Was zaehlt, ist die LISTE der Ueberlebenden.
|
|||
<span id="cb225-33"><a href="#cb225-33" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-34"><a href="#cb225-34" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
|
||||
<span id="cb225-35"><a href="#cb225-35" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-36"><a href="#cb225-36" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> json</span>
|
||||
<span id="cb225-37"><a href="#cb225-37" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> subprocess</span>
|
||||
<span id="cb225-38"><a href="#cb225-38" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
|
||||
<span id="cb225-39"><a href="#cb225-39" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> textwrap</span>
|
||||
<span id="cb225-36"><a href="#cb225-36" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> multiprocessing</span>
|
||||
<span id="cb225-37"><a href="#cb225-37" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> resource</span>
|
||||
<span id="cb225-38"><a href="#cb225-38" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
|
||||
<span id="cb225-39"><a href="#cb225-39" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> concurrent.futures <span class="im">import</span> ProcessPoolExecutor</span>
|
||||
<span id="cb225-40"><a href="#cb225-40" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-41"><a href="#cb225-41" aria-hidden="true" tabindex="-1"></a>GROESSEN <span class="op">=</span> [(<span class="dv">10</span>, <span class="dv">10</span>), (<span class="dv">32</span>, <span class="dv">32</span>), (<span class="dv">100</span>, <span class="dv">100</span>)] <span class="co"># (Lager, Kunden) -> 100 / 1.024 / 10.000 Variablen</span></span>
|
||||
<span id="cb225-41"><a href="#cb225-41" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
|
||||
<span id="cb225-42"><a href="#cb225-42" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-43"><a href="#cb225-43" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-44"><a href="#cb225-44" aria-hidden="true" tabindex="-1"></a><span class="co"># Jeder Eintrag ist ein eigenstaendiges Programm: Instanz aufbauen, loesen,</span></span>
|
||||
<span id="cb225-45"><a href="#cb225-45" aria-hidden="true" tabindex="-1"></a><span class="co"># Ergebnis als JSON ausgeben. Die Instanz wird in jedem Kindprozess aus</span></span>
|
||||
<span id="cb225-46"><a href="#cb225-46" aria-hidden="true" tabindex="-1"></a><span class="co"># derselben Saat neu erzeugt - so reist nichts ueber die Prozessgrenze,</span></span>
|
||||
<span id="cb225-47"><a href="#cb225-47" aria-hidden="true" tabindex="-1"></a><span class="co"># was das Ergebnis verfaelschen koennte.</span></span>
|
||||
<span id="cb225-48"><a href="#cb225-48" aria-hidden="true" tabindex="-1"></a>VORSPANN <span class="op">=</span> <span class="st">"""</span></span>
|
||||
<span id="cb225-49"><a href="#cb225-49" aria-hidden="true" tabindex="-1"></a><span class="st">import json, time, resource</span></span>
|
||||
<span id="cb225-50"><a href="#cb225-50" aria-hidden="true" tabindex="-1"></a><span class="st">import numpy as np</span></span>
|
||||
<span id="cb225-51"><a href="#cb225-51" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-52"><a href="#cb225-52" aria-hidden="true" tabindex="-1"></a><span class="st">def instanz(m, n):</span></span>
|
||||
<span id="cb225-53"><a href="#cb225-53" aria-hidden="true" tabindex="-1"></a><span class="st"> rng = np.random.default_rng(20)</span></span>
|
||||
<span id="cb225-54"><a href="#cb225-54" aria-hidden="true" tabindex="-1"></a><span class="st"> kosten = rng.integers(5, 95, (m, n)).astype(float)</span></span>
|
||||
<span id="cb225-55"><a href="#cb225-55" aria-hidden="true" tabindex="-1"></a><span class="st"> angebot = rng.integers(50, 150, m).astype(float)</span></span>
|
||||
<span id="cb225-56"><a href="#cb225-56" aria-hidden="true" tabindex="-1"></a><span class="st"> bedarf = angebot.sum() * rng.dirichlet(np.ones(n))</span></span>
|
||||
<span id="cb225-57"><a href="#cb225-57" aria-hidden="true" tabindex="-1"></a><span class="st"> return kosten, angebot, bedarf</span></span>
|
||||
<span id="cb225-43"><a href="#cb225-43" aria-hidden="true" tabindex="-1"></a>GROESSEN <span class="op">=</span> [(<span class="dv">10</span>, <span class="dv">10</span>), (<span class="dv">32</span>, <span class="dv">32</span>), (<span class="dv">100</span>, <span class="dv">100</span>)] <span class="co"># (Lager, Kunden) -> 100 / 1.024 / 10.000 Variablen</span></span>
|
||||
<span id="cb225-44"><a href="#cb225-44" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-45"><a href="#cb225-45" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-46"><a href="#cb225-46" aria-hidden="true" tabindex="-1"></a><span class="co"># Instanz und Speichermessung stehen als gewoehnliche Funktionen hier - nicht</span></span>
|
||||
<span id="cb225-47"><a href="#cb225-47" aria-hidden="true" tabindex="-1"></a><span class="co"># in einem String, den ein Kindprozess ausfuehrt. Jede Messfunktion baut die</span></span>
|
||||
<span id="cb225-48"><a href="#cb225-48" aria-hidden="true" tabindex="-1"></a><span class="co"># Instanz aus derselben Saat neu auf, damit ueber die Prozessgrenze nichts</span></span>
|
||||
<span id="cb225-49"><a href="#cb225-49" aria-hidden="true" tabindex="-1"></a><span class="co"># reist, was das Ergebnis verfaelschen koennte.</span></span>
|
||||
<span id="cb225-50"><a href="#cb225-50" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-51"><a href="#cb225-51" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> instanz(m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb225-52"><a href="#cb225-52" aria-hidden="true" tabindex="-1"></a> rng <span class="op">=</span> np.random.default_rng(<span class="dv">20</span>)</span>
|
||||
<span id="cb225-53"><a href="#cb225-53" aria-hidden="true" tabindex="-1"></a> kosten <span class="op">=</span> rng.integers(<span class="dv">5</span>, <span class="dv">95</span>, (m, n)).astype(<span class="bu">float</span>)</span>
|
||||
<span id="cb225-54"><a href="#cb225-54" aria-hidden="true" tabindex="-1"></a> angebot <span class="op">=</span> rng.integers(<span class="dv">50</span>, <span class="dv">150</span>, m).astype(<span class="bu">float</span>)</span>
|
||||
<span id="cb225-55"><a href="#cb225-55" aria-hidden="true" tabindex="-1"></a> bedarf <span class="op">=</span> angebot.<span class="bu">sum</span>() <span class="op">*</span> rng.dirichlet(np.ones(n))</span>
|
||||
<span id="cb225-56"><a href="#cb225-56" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> kosten, angebot, bedarf</span>
|
||||
<span id="cb225-57"><a href="#cb225-57" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-58"><a href="#cb225-58" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-59"><a href="#cb225-59" aria-hidden="true" tabindex="-1"></a><span class="st">def speicher_mb():</span></span>
|
||||
<span id="cb225-60"><a href="#cb225-60" aria-hidden="true" tabindex="-1"></a><span class="st"> # ru_maxrss ist unter Linux in Kilobyte</span></span>
|
||||
<span id="cb225-61"><a href="#cb225-61" aria-hidden="true" tabindex="-1"></a><span class="st"> return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024</span></span>
|
||||
<span id="cb225-62"><a href="#cb225-62" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-63"><a href="#cb225-63" aria-hidden="true" tabindex="-1"></a><span class="st">M, N = </span><span class="sc">{m}</span><span class="st">, </span><span class="sc">{n}</span></span>
|
||||
<span id="cb225-64"><a href="#cb225-64" aria-hidden="true" tabindex="-1"></a><span class="st">kosten, angebot, bedarf = instanz(M, N)</span></span>
|
||||
<span id="cb225-65"><a href="#cb225-65" aria-hidden="true" tabindex="-1"></a><span class="st">"""</span></span>
|
||||
<span id="cb225-66"><a href="#cb225-66" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-67"><a href="#cb225-67" aria-hidden="true" tabindex="-1"></a>ANSAETZE <span class="op">=</span> {</span>
|
||||
<span id="cb225-68"><a href="#cb225-68" aria-hidden="true" tabindex="-1"></a> <span class="st">"scipy.linprog"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb225-69"><a href="#cb225-69" aria-hidden="true" tabindex="-1"></a><span class="st"> from scipy.optimize import linprog</span></span>
|
||||
<span id="cb225-70"><a href="#cb225-70" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter()</span></span>
|
||||
<span id="cb225-71"><a href="#cb225-71" aria-hidden="true" tabindex="-1"></a><span class="st"> c = kosten.reshape(-1)</span></span>
|
||||
<span id="cb225-72"><a href="#cb225-72" aria-hidden="true" tabindex="-1"></a><span class="st"> A_ub = np.zeros((M, M * N)); A_eq = np.zeros((N, M * N))</span></span>
|
||||
<span id="cb225-73"><a href="#cb225-73" aria-hidden="true" tabindex="-1"></a><span class="st"> for i in range(M):</span></span>
|
||||
<span id="cb225-74"><a href="#cb225-74" aria-hidden="true" tabindex="-1"></a><span class="st"> A_ub[i, i * N:(i + 1) * N] = 1.0</span></span>
|
||||
<span id="cb225-75"><a href="#cb225-75" aria-hidden="true" tabindex="-1"></a><span class="st"> for j in range(N):</span></span>
|
||||
<span id="cb225-76"><a href="#cb225-76" aria-hidden="true" tabindex="-1"></a><span class="st"> A_eq[j, j::N] = 1.0</span></span>
|
||||
<span id="cb225-77"><a href="#cb225-77" aria-hidden="true" tabindex="-1"></a><span class="st"> aufbau = time.perf_counter() - t0</span></span>
|
||||
<span id="cb225-78"><a href="#cb225-78" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter()</span></span>
|
||||
<span id="cb225-79"><a href="#cb225-79" aria-hidden="true" tabindex="-1"></a><span class="st"> r = linprog(c=c, A_ub=A_ub, b_ub=angebot, A_eq=A_eq, b_eq=bedarf,</span></span>
|
||||
<span id="cb225-80"><a href="#cb225-80" aria-hidden="true" tabindex="-1"></a><span class="st"> bounds=(0, None), method="highs")</span></span>
|
||||
<span id="cb225-81"><a href="#cb225-81" aria-hidden="true" tabindex="-1"></a><span class="st"> loesen = time.perf_counter() - t0</span></span>
|
||||
<span id="cb225-82"><a href="#cb225-82" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (float(r.fun), aufbau, loesen, speicher_mb())</span></span>
|
||||
<span id="cb225-83"><a href="#cb225-83" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb225-84"><a href="#cb225-84" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-85"><a href="#cb225-85" aria-hidden="true" tabindex="-1"></a> <span class="st">"highspy"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb225-86"><a href="#cb225-86" aria-hidden="true" tabindex="-1"></a><span class="st"> import highspy</span></span>
|
||||
<span id="cb225-87"><a href="#cb225-87" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter()</span></span>
|
||||
<span id="cb225-88"><a href="#cb225-88" aria-hidden="true" tabindex="-1"></a><span class="st"> h = highspy.Highs(); h.setOptionValue("output_flag", False)</span></span>
|
||||
<span id="cb225-89"><a href="#cb225-89" aria-hidden="true" tabindex="-1"></a><span class="st"> h.addVars(M * N, np.zeros(M * N), np.full(M * N, highspy.kHighsInf))</span></span>
|
||||
<span id="cb225-90"><a href="#cb225-90" aria-hidden="true" tabindex="-1"></a><span class="st"> for k in range(M * N):</span></span>
|
||||
<span id="cb225-91"><a href="#cb225-91" aria-hidden="true" tabindex="-1"></a><span class="st"> h.changeColCost(k, float(kosten.reshape(-1)[k]))</span></span>
|
||||
<span id="cb225-92"><a href="#cb225-92" aria-hidden="true" tabindex="-1"></a><span class="st"> for i in range(M):</span></span>
|
||||
<span id="cb225-93"><a href="#cb225-93" aria-hidden="true" tabindex="-1"></a><span class="st"> idx = np.arange(i * N, (i + 1) * N, dtype=np.int32)</span></span>
|
||||
<span id="cb225-94"><a href="#cb225-94" aria-hidden="true" tabindex="-1"></a><span class="st"> h.addRow(-highspy.kHighsInf, float(angebot[i]), N, idx, np.ones(N))</span></span>
|
||||
<span id="cb225-95"><a href="#cb225-95" aria-hidden="true" tabindex="-1"></a><span class="st"> for j in range(N):</span></span>
|
||||
<span id="cb225-96"><a href="#cb225-96" aria-hidden="true" tabindex="-1"></a><span class="st"> idx = np.arange(j, M * N, N, dtype=np.int32)</span></span>
|
||||
<span id="cb225-97"><a href="#cb225-97" aria-hidden="true" tabindex="-1"></a><span class="st"> h.addRow(float(bedarf[j]), float(bedarf[j]), M, idx, np.ones(M))</span></span>
|
||||
<span id="cb225-98"><a href="#cb225-98" aria-hidden="true" tabindex="-1"></a><span class="st"> aufbau = time.perf_counter() - t0</span></span>
|
||||
<span id="cb225-99"><a href="#cb225-99" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter(); h.run(); loesen = time.perf_counter() - t0</span></span>
|
||||
<span id="cb225-100"><a href="#cb225-100" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (h.getInfo().objective_function_value, aufbau, loesen, speicher_mb())</span></span>
|
||||
<span id="cb225-101"><a href="#cb225-101" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb225-102"><a href="#cb225-102" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-103"><a href="#cb225-103" aria-hidden="true" tabindex="-1"></a> <span class="st">"ortools/GLOP"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb225-104"><a href="#cb225-104" aria-hidden="true" tabindex="-1"></a><span class="st"> from ortools.linear_solver import pywraplp</span></span>
|
||||
<span id="cb225-105"><a href="#cb225-105" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter()</span></span>
|
||||
<span id="cb225-106"><a href="#cb225-106" aria-hidden="true" tabindex="-1"></a><span class="st"> s = pywraplp.Solver.CreateSolver("GLOP")</span></span>
|
||||
<span id="cb225-107"><a href="#cb225-107" aria-hidden="true" tabindex="-1"></a><span class="st"> x = [[s.NumVar(0, s.infinity(), f"x</span><span class="sc">{i}</span><span class="st">_</span><span class="sc">{j}</span><span class="st">") for j in range(N)]</span></span>
|
||||
<span id="cb225-108"><a href="#cb225-108" aria-hidden="true" tabindex="-1"></a><span class="st"> for i in range(M)]</span></span>
|
||||
<span id="cb225-109"><a href="#cb225-109" aria-hidden="true" tabindex="-1"></a><span class="st"> for i in range(M):</span></span>
|
||||
<span id="cb225-110"><a href="#cb225-110" aria-hidden="true" tabindex="-1"></a><span class="st"> s.Add(sum(x[i]) <= float(angebot[i]))</span></span>
|
||||
<span id="cb225-111"><a href="#cb225-111" aria-hidden="true" tabindex="-1"></a><span class="st"> for j in range(N):</span></span>
|
||||
<span id="cb225-112"><a href="#cb225-112" aria-hidden="true" tabindex="-1"></a><span class="st"> s.Add(sum(x[i][j] for i in range(M)) == float(bedarf[j]))</span></span>
|
||||
<span id="cb225-113"><a href="#cb225-113" aria-hidden="true" tabindex="-1"></a><span class="st"> s.Minimize(sum(float(kosten[i, j]) * x[i][j]</span></span>
|
||||
<span id="cb225-114"><a href="#cb225-114" aria-hidden="true" tabindex="-1"></a><span class="st"> for i in range(M) for j in range(N)))</span></span>
|
||||
<span id="cb225-115"><a href="#cb225-115" aria-hidden="true" tabindex="-1"></a><span class="st"> aufbau = time.perf_counter() - t0</span></span>
|
||||
<span id="cb225-116"><a href="#cb225-116" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter(); s.Solve(); loesen = time.perf_counter() - t0</span></span>
|
||||
<span id="cb225-117"><a href="#cb225-117" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (s.Objective().Value(), aufbau, loesen, speicher_mb())</span></span>
|
||||
<span id="cb225-118"><a href="#cb225-118" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb225-59"><a href="#cb225-59" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> speicher_mb() <span class="op">-></span> <span class="bu">float</span>:</span>
|
||||
<span id="cb225-60"><a href="#cb225-60" aria-hidden="true" tabindex="-1"></a> <span class="co"># ru_maxrss ist unter Linux in Kilobyte. Gemessen wird der Kindprozess -</span></span>
|
||||
<span id="cb225-61"><a href="#cb225-61" aria-hidden="true" tabindex="-1"></a> <span class="co"># deshalb muss jede Messung einen eigenen bekommen.</span></span>
|
||||
<span id="cb225-62"><a href="#cb225-62" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss <span class="op">/</span> <span class="dv">1024</span></span>
|
||||
<span id="cb225-63"><a href="#cb225-63" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-64"><a href="#cb225-64" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-65"><a href="#cb225-65" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe_scipy(m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb225-66"><a href="#cb225-66" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> scipy.optimize <span class="im">import</span> linprog</span>
|
||||
<span id="cb225-67"><a href="#cb225-67" aria-hidden="true" tabindex="-1"></a> kosten, angebot, bedarf <span class="op">=</span> instanz(m, n)</span>
|
||||
<span id="cb225-68"><a href="#cb225-68" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb225-69"><a href="#cb225-69" aria-hidden="true" tabindex="-1"></a> c <span class="op">=</span> kosten.reshape(<span class="op">-</span><span class="dv">1</span>)</span>
|
||||
<span id="cb225-70"><a href="#cb225-70" aria-hidden="true" tabindex="-1"></a> A_ub <span class="op">=</span> np.zeros((m, m <span class="op">*</span> n))<span class="op">;</span> A_eq <span class="op">=</span> np.zeros((n, m <span class="op">*</span> n))</span>
|
||||
<span id="cb225-71"><a href="#cb225-71" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m):</span>
|
||||
<span id="cb225-72"><a href="#cb225-72" aria-hidden="true" tabindex="-1"></a> A_ub[i, i <span class="op">*</span> n:(i <span class="op">+</span> <span class="dv">1</span>) <span class="op">*</span> n] <span class="op">=</span> <span class="fl">1.0</span></span>
|
||||
<span id="cb225-73"><a href="#cb225-73" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(n):</span>
|
||||
<span id="cb225-74"><a href="#cb225-74" aria-hidden="true" tabindex="-1"></a> A_eq[j, j::n] <span class="op">=</span> <span class="fl">1.0</span></span>
|
||||
<span id="cb225-75"><a href="#cb225-75" aria-hidden="true" tabindex="-1"></a> aufbau <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb225-76"><a href="#cb225-76" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb225-77"><a href="#cb225-77" aria-hidden="true" tabindex="-1"></a> r <span class="op">=</span> linprog(c<span class="op">=</span>c, A_ub<span class="op">=</span>A_ub, b_ub<span class="op">=</span>angebot, A_eq<span class="op">=</span>A_eq, b_eq<span class="op">=</span>bedarf,</span>
|
||||
<span id="cb225-78"><a href="#cb225-78" aria-hidden="true" tabindex="-1"></a> bounds<span class="op">=</span>(<span class="dv">0</span>, <span class="va">None</span>), method<span class="op">=</span><span class="st">"highs"</span>)</span>
|
||||
<span id="cb225-79"><a href="#cb225-79" aria-hidden="true" tabindex="-1"></a> loesen <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb225-80"><a href="#cb225-80" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">float</span>(r.fun), aufbau, loesen, speicher_mb()</span>
|
||||
<span id="cb225-81"><a href="#cb225-81" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-82"><a href="#cb225-82" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-83"><a href="#cb225-83" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe_highspy(m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb225-84"><a href="#cb225-84" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> highspy</span>
|
||||
<span id="cb225-85"><a href="#cb225-85" aria-hidden="true" tabindex="-1"></a> kosten, angebot, bedarf <span class="op">=</span> instanz(m, n)</span>
|
||||
<span id="cb225-86"><a href="#cb225-86" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb225-87"><a href="#cb225-87" aria-hidden="true" tabindex="-1"></a> h <span class="op">=</span> highspy.Highs()<span class="op">;</span> h.setOptionValue(<span class="st">"output_flag"</span>, <span class="va">False</span>)</span>
|
||||
<span id="cb225-88"><a href="#cb225-88" aria-hidden="true" tabindex="-1"></a> h.addVars(m <span class="op">*</span> n, np.zeros(m <span class="op">*</span> n), np.full(m <span class="op">*</span> n, highspy.kHighsInf))</span>
|
||||
<span id="cb225-89"><a href="#cb225-89" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(m <span class="op">*</span> n):</span>
|
||||
<span id="cb225-90"><a href="#cb225-90" aria-hidden="true" tabindex="-1"></a> h.changeColCost(k, <span class="bu">float</span>(kosten.reshape(<span class="op">-</span><span class="dv">1</span>)[k]))</span>
|
||||
<span id="cb225-91"><a href="#cb225-91" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m):</span>
|
||||
<span id="cb225-92"><a href="#cb225-92" aria-hidden="true" tabindex="-1"></a> idx <span class="op">=</span> np.arange(i <span class="op">*</span> n, (i <span class="op">+</span> <span class="dv">1</span>) <span class="op">*</span> n, dtype<span class="op">=</span>np.int32)</span>
|
||||
<span id="cb225-93"><a href="#cb225-93" aria-hidden="true" tabindex="-1"></a> h.addRow(<span class="op">-</span>highspy.kHighsInf, <span class="bu">float</span>(angebot[i]), n, idx, np.ones(n))</span>
|
||||
<span id="cb225-94"><a href="#cb225-94" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(n):</span>
|
||||
<span id="cb225-95"><a href="#cb225-95" aria-hidden="true" tabindex="-1"></a> idx <span class="op">=</span> np.arange(j, m <span class="op">*</span> n, n, dtype<span class="op">=</span>np.int32)</span>
|
||||
<span id="cb225-96"><a href="#cb225-96" aria-hidden="true" tabindex="-1"></a> h.addRow(<span class="bu">float</span>(bedarf[j]), <span class="bu">float</span>(bedarf[j]), m, idx, np.ones(m))</span>
|
||||
<span id="cb225-97"><a href="#cb225-97" aria-hidden="true" tabindex="-1"></a> aufbau <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb225-98"><a href="#cb225-98" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()<span class="op">;</span> h.run()<span class="op">;</span> loesen <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb225-99"><a href="#cb225-99" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> h.getInfo().objective_function_value, aufbau, loesen, speicher_mb()</span>
|
||||
<span id="cb225-100"><a href="#cb225-100" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-101"><a href="#cb225-101" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-102"><a href="#cb225-102" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe_ortools(m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb225-103"><a href="#cb225-103" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> ortools.linear_solver <span class="im">import</span> pywraplp</span>
|
||||
<span id="cb225-104"><a href="#cb225-104" aria-hidden="true" tabindex="-1"></a> kosten, angebot, bedarf <span class="op">=</span> instanz(m, n)</span>
|
||||
<span id="cb225-105"><a href="#cb225-105" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb225-106"><a href="#cb225-106" aria-hidden="true" tabindex="-1"></a> s <span class="op">=</span> pywraplp.Solver.CreateSolver(<span class="st">"GLOP"</span>)</span>
|
||||
<span id="cb225-107"><a href="#cb225-107" 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>i<span class="sc">}</span><span class="ss">_</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>(n)]</span>
|
||||
<span id="cb225-108"><a href="#cb225-108" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m)]</span>
|
||||
<span id="cb225-109"><a href="#cb225-109" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m):</span>
|
||||
<span id="cb225-110"><a href="#cb225-110" aria-hidden="true" tabindex="-1"></a> s.Add(<span class="bu">sum</span>(x[i]) <span class="op"><=</span> <span class="bu">float</span>(angebot[i]))</span>
|
||||
<span id="cb225-111"><a href="#cb225-111" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(n):</span>
|
||||
<span id="cb225-112"><a href="#cb225-112" aria-hidden="true" tabindex="-1"></a> s.Add(<span class="bu">sum</span>(x[i][j] <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m)) <span class="op">==</span> <span class="bu">float</span>(bedarf[j]))</span>
|
||||
<span id="cb225-113"><a href="#cb225-113" aria-hidden="true" tabindex="-1"></a> s.Minimize(<span class="bu">sum</span>(<span class="bu">float</span>(kosten[i, j]) <span class="op">*</span> x[i][j]</span>
|
||||
<span id="cb225-114"><a href="#cb225-114" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m) <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(n)))</span>
|
||||
<span id="cb225-115"><a href="#cb225-115" aria-hidden="true" tabindex="-1"></a> aufbau <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb225-116"><a href="#cb225-116" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()<span class="op">;</span> s.Solve()<span class="op">;</span> loesen <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb225-117"><a href="#cb225-117" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> s.Objective().Value(), aufbau, loesen, speicher_mb()</span>
|
||||
<span id="cb225-118"><a href="#cb225-118" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-119"><a href="#cb225-119" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-120"><a href="#cb225-120" aria-hidden="true" tabindex="-1"></a> <span class="st">"cvxpy"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb225-121"><a href="#cb225-121" aria-hidden="true" tabindex="-1"></a><span class="st"> import cvxpy as cp</span></span>
|
||||
<span id="cb225-122"><a href="#cb225-122" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter()</span></span>
|
||||
<span id="cb225-123"><a href="#cb225-123" aria-hidden="true" tabindex="-1"></a><span class="st"> x = cp.Variable((M, N), nonneg=True)</span></span>
|
||||
<span id="cb225-124"><a href="#cb225-124" aria-hidden="true" tabindex="-1"></a><span class="st"> problem = cp.Problem(cp.Minimize(cp.sum(cp.multiply(kosten, x))),</span></span>
|
||||
<span id="cb225-125"><a href="#cb225-125" aria-hidden="true" tabindex="-1"></a><span class="st"> [cp.sum(x, axis=1) <= angebot,</span></span>
|
||||
<span id="cb225-126"><a href="#cb225-126" aria-hidden="true" tabindex="-1"></a><span class="st"> cp.sum(x, axis=0) == bedarf])</span></span>
|
||||
<span id="cb225-127"><a href="#cb225-127" aria-hidden="true" tabindex="-1"></a><span class="st"> aufbau = time.perf_counter() - t0</span></span>
|
||||
<span id="cb225-128"><a href="#cb225-128" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter(); problem.solve(); loesen = time.perf_counter() - t0</span></span>
|
||||
<span id="cb225-129"><a href="#cb225-129" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (float(problem.value), aufbau, loesen, speicher_mb())</span></span>
|
||||
<span id="cb225-130"><a href="#cb225-130" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb225-131"><a href="#cb225-131" aria-hidden="true" tabindex="-1"></a>}</span>
|
||||
<span id="cb225-120"><a href="#cb225-120" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe_cvxpy(m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb225-121"><a href="#cb225-121" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> cvxpy <span class="im">as</span> cp</span>
|
||||
<span id="cb225-122"><a href="#cb225-122" aria-hidden="true" tabindex="-1"></a> kosten, angebot, bedarf <span class="op">=</span> instanz(m, n)</span>
|
||||
<span id="cb225-123"><a href="#cb225-123" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb225-124"><a href="#cb225-124" aria-hidden="true" tabindex="-1"></a> x <span class="op">=</span> cp.Variable((m, n), nonneg<span class="op">=</span><span class="va">True</span>)</span>
|
||||
<span id="cb225-125"><a href="#cb225-125" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> cp.Problem(cp.Minimize(cp.<span class="bu">sum</span>(cp.multiply(kosten, x))),</span>
|
||||
<span id="cb225-126"><a href="#cb225-126" aria-hidden="true" tabindex="-1"></a> [cp.<span class="bu">sum</span>(x, axis<span class="op">=</span><span class="dv">1</span>) <span class="op"><=</span> angebot,</span>
|
||||
<span id="cb225-127"><a href="#cb225-127" aria-hidden="true" tabindex="-1"></a> cp.<span class="bu">sum</span>(x, axis<span class="op">=</span><span class="dv">0</span>) <span class="op">==</span> bedarf])</span>
|
||||
<span id="cb225-128"><a href="#cb225-128" aria-hidden="true" tabindex="-1"></a> aufbau <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb225-129"><a href="#cb225-129" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()<span class="op">;</span> problem.solve()<span class="op">;</span> loesen <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb225-130"><a href="#cb225-130" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">float</span>(problem.value), aufbau, loesen, speicher_mb()</span>
|
||||
<span id="cb225-131"><a href="#cb225-131" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-132"><a href="#cb225-132" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-133"><a href="#cb225-133" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-134"><a href="#cb225-134" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe(name: <span class="bu">str</span>, quelltext: <span class="bu">str</span>, m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb225-135"><a href="#cb225-135" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Fuehrt einen Ansatz in einem eigenen Prozess aus."""</span></span>
|
||||
<span id="cb225-136"><a href="#cb225-136" aria-hidden="true" tabindex="-1"></a> programm <span class="op">=</span> (VORSPANN.<span class="bu">format</span>(m<span class="op">=</span>m, n<span class="op">=</span>n) <span class="op">+</span> textwrap.dedent(quelltext)</span>
|
||||
<span id="cb225-137"><a href="#cb225-137" aria-hidden="true" tabindex="-1"></a> <span class="op">+</span> <span class="st">"</span><span class="ch">\n</span><span class="st">print(json.dumps(ausgabe))</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
||||
<span id="cb225-138"><a href="#cb225-138" aria-hidden="true" tabindex="-1"></a> ergebnis <span class="op">=</span> subprocess.run([sys.executable, <span class="st">"-c"</span>, programm],</span>
|
||||
<span id="cb225-139"><a href="#cb225-139" 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">600</span>)</span>
|
||||
<span id="cb225-140"><a href="#cb225-140" 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="cb225-141"><a href="#cb225-141" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span>, ergebnis.stderr.strip().splitlines()[<span class="op">-</span><span class="dv">1</span>][:<span class="dv">60</span>]</span>
|
||||
<span id="cb225-142"><a href="#cb225-142" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> json.loads(ergebnis.stdout.strip().splitlines()[<span class="op">-</span><span class="dv">1</span>]), <span class="va">None</span></span>
|
||||
<span id="cb225-143"><a href="#cb225-143" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-144"><a href="#cb225-144" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-145"><a href="#cb225-145" 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="cb225-146"><a href="#cb225-146" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb225-147"><a href="#cb225-147" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" SKALIERUNGSVERGLEICH: TRANSPORTPROBLEM, VIER BIBLIOTHEKEN"</span>)</span>
|
||||
<span id="cb225-148"><a href="#cb225-148" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb225-149"><a href="#cb225-149" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Jede Zeile ein eigener Prozess. Zeiten und Speicher sind "</span></span>
|
||||
<span id="cb225-150"><a href="#cb225-150" aria-hidden="true" tabindex="-1"></a> <span class="st">"hardwareabhaengig,"</span>)</span>
|
||||
<span id="cb225-151"><a href="#cb225-151" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"die Zielwerte und ihr Verhaeltnis zueinander nicht.</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
||||
<span id="cb225-152"><a href="#cb225-152" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-153"><a href="#cb225-153" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> m, n <span class="kw">in</span> GROESSEN:</span>
|
||||
<span id="cb225-154"><a href="#cb225-154" aria-hidden="true" tabindex="-1"></a> kopf <span class="op">=</span> <span class="ss">f"--- </span><span class="sc">{</span>m<span class="sc">}</span><span class="ss"> Lager x </span><span class="sc">{</span>n<span class="sc">}</span><span class="ss"> Kunden = </span><span class="sc">{</span>m <span class="op">*</span> n<span class="sc">:,}</span><span class="ss"> Variablen "</span></span>
|
||||
<span id="cb225-155"><a href="#cb225-155" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(kopf <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="bu">max</span>(<span class="dv">3</span>, <span class="dv">92</span> <span class="op">-</span> <span class="bu">len</span>(kopf)))</span>
|
||||
<span id="cb225-156"><a href="#cb225-156" 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">:<16}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Zielwert'</span><span class="sc">:>14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Aufbau'</span><span class="sc">:>9}</span><span class="ss"> "</span></span>
|
||||
<span id="cb225-157"><a href="#cb225-157" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'Loesen'</span><span class="sc">:>9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Anteil'</span><span class="sc">:>8}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Speicher'</span><span class="sc">:>10}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb225-158"><a href="#cb225-158" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" "</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">72</span>)</span>
|
||||
<span id="cb225-159"><a href="#cb225-159" aria-hidden="true" tabindex="-1"></a> zielwerte <span class="op">=</span> {}</span>
|
||||
<span id="cb225-160"><a href="#cb225-160" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, quelltext <span class="kw">in</span> ANSAETZE.items():</span>
|
||||
<span id="cb225-161"><a href="#cb225-161" aria-hidden="true" tabindex="-1"></a> werte, fehler <span class="op">=</span> messe(name, quelltext, m, n)</span>
|
||||
<span id="cb225-162"><a href="#cb225-162" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> werte <span class="kw">is</span> <span class="va">None</span>:</span>
|
||||
<span id="cb225-163"><a href="#cb225-163" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>name<span class="sc">:<16}</span><span class="ss"> nicht verfuegbar: </span><span class="sc">{</span>fehler<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb225-164"><a href="#cb225-164" aria-hidden="true" tabindex="-1"></a> <span class="cf">continue</span></span>
|
||||
<span id="cb225-165"><a href="#cb225-165" aria-hidden="true" tabindex="-1"></a> ziel, aufbau, loesen, speicher <span class="op">=</span> werte</span>
|
||||
<span id="cb225-166"><a href="#cb225-166" aria-hidden="true" tabindex="-1"></a> zielwerte[name] <span class="op">=</span> ziel</span>
|
||||
<span id="cb225-167"><a href="#cb225-167" aria-hidden="true" tabindex="-1"></a> anteil <span class="op">=</span> aufbau <span class="op">/</span> (aufbau <span class="op">+</span> loesen) <span class="op">*</span> <span class="dv">100</span></span>
|
||||
<span id="cb225-168"><a href="#cb225-168" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>name<span class="sc">:<16}</span><span class="ss"> </span><span class="sc">{</span>ziel<span class="sc">:>14,.2f}</span><span class="ss"> </span><span class="sc">{</span>aufbau<span class="sc">:>8.3f}</span><span class="ss">s "</span></span>
|
||||
<span id="cb225-169"><a href="#cb225-169" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>loesen<span class="sc">:>8.3f}</span><span class="ss">s </span><span class="sc">{</span>anteil<span class="sc">:>7.0f}</span><span class="ss">% </span><span class="sc">{</span>speicher<span class="sc">:>9.0f}</span><span class="ss"> MB"</span>)</span>
|
||||
<span id="cb225-170"><a href="#cb225-170" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-171"><a href="#cb225-171" aria-hidden="true" tabindex="-1"></a> <span class="co"># Die wichtigste Zeile: Rechnen alle dasselbe aus?</span></span>
|
||||
<span id="cb225-172"><a href="#cb225-172" aria-hidden="true" tabindex="-1"></a> spanne <span class="op">=</span> <span class="bu">max</span>(zielwerte.values()) <span class="op">-</span> <span class="bu">min</span>(zielwerte.values())</span>
|
||||
<span id="cb225-173"><a href="#cb225-173" aria-hidden="true" tabindex="-1"></a> bezug <span class="op">=</span> <span class="bu">max</span>(<span class="bu">abs</span>(v) <span class="cf">for</span> v <span class="kw">in</span> zielwerte.values())</span>
|
||||
<span id="cb225-174"><a href="#cb225-174" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">''</span><span class="sc">:16}</span><span class="ss"> Spannweite der Zielwerte: </span><span class="sc">{</span>spanne<span class="sc">:.2e}</span><span class="ss"> "</span></span>
|
||||
<span id="cb225-175"><a href="#cb225-175" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"(relativ </span><span class="sc">{</span>spanne <span class="op">/</span> bezug<span class="sc">:.1e}</span><span class="ss">)"</span>)</span>
|
||||
<span id="cb225-176"><a href="#cb225-176" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> spanne <span class="op">/</span> bezug <span class="op">></span> <span class="fl">1e-6</span>:</span>
|
||||
<span id="cb225-177"><a href="#cb225-177" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ACHTUNG: Die Bibliotheken widersprechen sich - "</span></span>
|
||||
<span id="cb225-178"><a href="#cb225-178" aria-hidden="true" tabindex="-1"></a> <span class="st">"der Zeitvergleich ist wertlos."</span>)</span>
|
||||
<span id="cb225-179"><a href="#cb225-179" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb225-180"><a href="#cb225-180" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-181"><a href="#cb225-181" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb225-182"><a href="#cb225-182" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" WAS MAN AUS SO EINER TABELLE ABLESEN DARF - UND WAS NICHT"</span>)</span>
|
||||
<span id="cb225-183"><a href="#cb225-183" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb225-184"><a href="#cb225-184" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"DARF man ablesen:"</span>)</span>
|
||||
<span id="cb225-185"><a href="#cb225-185" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Die Spalte 'Anteil' - wie viel der Zeit in den AUFBAU geht statt"</span>)</span>
|
||||
<span id="cb225-186"><a href="#cb225-186" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ins Loesen. Wenn dort 80 </span><span class="sc">% s</span><span class="st">tehen, ist ein schnellerer Solver die"</span>)</span>
|
||||
<span id="cb225-187"><a href="#cb225-187" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" falsche Antwort; dann gehoert das Modell vektorisiert aufgebaut"</span>)</span>
|
||||
<span id="cb225-188"><a href="#cb225-188" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" (Kapitel Oekosystem)."</span>)</span>
|
||||
<span id="cb225-189"><a href="#cb225-189" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Die Groessenordnung des Speicherbedarfs. Sie entscheidet, was auf"</span>)</span>
|
||||
<span id="cb225-190"><a href="#cb225-190" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" einer bestimmten Maschine ueberhaupt laeuft."</span>)</span>
|
||||
<span id="cb225-191"><a href="#cb225-191" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Wie sich beides mit der Groesse ENTWICKELT. Der Trend ist"</span>)</span>
|
||||
<span id="cb225-192"><a href="#cb225-192" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" uebertragbarer als der Absolutwert."</span>)</span>
|
||||
<span id="cb225-193"><a href="#cb225-193" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb225-194"><a href="#cb225-194" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"NICHT ablesen darf man:"</span>)</span>
|
||||
<span id="cb225-195"><a href="#cb225-195" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * 'Bibliothek X ist schneller als Y.' Gemessen wurde EIN"</span>)</span>
|
||||
<span id="cb225-196"><a href="#cb225-196" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Problemtyp in EINER Formulierung. Ein MILP, ein QP oder eine"</span>)</span>
|
||||
<span id="cb225-197"><a href="#cb225-197" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" andere Modellierung desselben Problems koennen die Reihenfolge"</span>)</span>
|
||||
<span id="cb225-198"><a href="#cb225-198" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" umdrehen."</span>)</span>
|
||||
<span id="cb225-199"><a href="#cb225-199" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Etwas ueber Ihre Maschine. Diese Zahlen stammen von einer"</span>)</span>
|
||||
<span id="cb225-200"><a href="#cb225-200" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" anderen. Der Sinn des Programms ist, dass Sie es auf Ihrer"</span>)</span>
|
||||
<span id="cb225-201"><a href="#cb225-201" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" laufen lassen."</span>)</span>
|
||||
<span id="cb225-202"><a href="#cb225-202" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span></code></pre></div>
|
||||
<span id="cb225-133"><a href="#cb225-133" aria-hidden="true" tabindex="-1"></a>ANSAETZE <span class="op">=</span> {<span class="st">"scipy.linprog"</span>: messe_scipy, <span class="st">"highspy"</span>: messe_highspy,</span>
|
||||
<span id="cb225-134"><a href="#cb225-134" aria-hidden="true" tabindex="-1"></a> <span class="st">"ortools/GLOP"</span>: messe_ortools, <span class="st">"cvxpy"</span>: messe_cvxpy}</span>
|
||||
<span id="cb225-135"><a href="#cb225-135" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-136"><a href="#cb225-136" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-137"><a href="#cb225-137" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe(funktion, m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb225-138"><a href="#cb225-138" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Fuehrt eine Messfunktion in einem FRISCHEN Prozess aus.</span></span>
|
||||
<span id="cb225-139"><a href="#cb225-139" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-140"><a href="#cb225-140" aria-hidden="true" tabindex="-1"></a><span class="co"> 'spawn' und max_tasks_per_child=1 zusammen garantieren, was Regel 1</span></span>
|
||||
<span id="cb225-141"><a href="#cb225-141" aria-hidden="true" tabindex="-1"></a><span class="co"> verlangt: Jede Messung sieht einen leeren Interpreter. Ohne das</span></span>
|
||||
<span id="cb225-142"><a href="#cb225-142" aria-hidden="true" tabindex="-1"></a><span class="co"> zweite wuerde der Pool seinen Arbeiter wiederverwenden - dann waere</span></span>
|
||||
<span id="cb225-143"><a href="#cb225-143" aria-hidden="true" tabindex="-1"></a><span class="co"> der Speicherwert der zweiten Bibliothek um die erste zu hoch, und</span></span>
|
||||
<span id="cb225-144"><a href="#cb225-144" aria-hidden="true" tabindex="-1"></a><span class="co"> ortools und highspy saessen im selben Prozess.</span></span>
|
||||
<span id="cb225-145"><a href="#cb225-145" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
|
||||
<span id="cb225-146"><a href="#cb225-146" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> ProcessPoolExecutor(</span>
|
||||
<span id="cb225-147"><a href="#cb225-147" aria-hidden="true" tabindex="-1"></a> max_workers<span class="op">=</span><span class="dv">1</span>,</span>
|
||||
<span id="cb225-148"><a href="#cb225-148" aria-hidden="true" tabindex="-1"></a> mp_context<span class="op">=</span>multiprocessing.get_context(<span class="st">"spawn"</span>),</span>
|
||||
<span id="cb225-149"><a href="#cb225-149" 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="cb225-150"><a href="#cb225-150" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span>:</span>
|
||||
<span id="cb225-151"><a href="#cb225-151" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> pool.submit(funktion, m, n).result(timeout<span class="op">=</span><span class="dv">600</span>), <span class="va">None</span></span>
|
||||
<span id="cb225-152"><a href="#cb225-152" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> <span class="pp">Exception</span> <span class="im">as</span> fehler:</span>
|
||||
<span id="cb225-153"><a href="#cb225-153" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span>, <span class="bu">str</span>(fehler).strip().splitlines()[<span class="op">-</span><span class="dv">1</span>][:<span class="dv">60</span>]</span>
|
||||
<span id="cb225-154"><a href="#cb225-154" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-155"><a href="#cb225-155" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-156"><a href="#cb225-156" 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="cb225-157"><a href="#cb225-157" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb225-158"><a href="#cb225-158" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" SKALIERUNGSVERGLEICH: TRANSPORTPROBLEM, VIER BIBLIOTHEKEN"</span>)</span>
|
||||
<span id="cb225-159"><a href="#cb225-159" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb225-160"><a href="#cb225-160" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Jede Zeile ein eigener Prozess. Zeiten und Speicher sind "</span></span>
|
||||
<span id="cb225-161"><a href="#cb225-161" aria-hidden="true" tabindex="-1"></a> <span class="st">"hardwareabhaengig,"</span>)</span>
|
||||
<span id="cb225-162"><a href="#cb225-162" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"die Zielwerte und ihr Verhaeltnis zueinander nicht.</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
||||
<span id="cb225-163"><a href="#cb225-163" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-164"><a href="#cb225-164" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> m, n <span class="kw">in</span> GROESSEN:</span>
|
||||
<span id="cb225-165"><a href="#cb225-165" aria-hidden="true" tabindex="-1"></a> kopf <span class="op">=</span> <span class="ss">f"--- </span><span class="sc">{</span>m<span class="sc">}</span><span class="ss"> Lager x </span><span class="sc">{</span>n<span class="sc">}</span><span class="ss"> Kunden = </span><span class="sc">{</span>m <span class="op">*</span> n<span class="sc">:,}</span><span class="ss"> Variablen "</span></span>
|
||||
<span id="cb225-166"><a href="#cb225-166" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(kopf <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="bu">max</span>(<span class="dv">3</span>, <span class="dv">92</span> <span class="op">-</span> <span class="bu">len</span>(kopf)))</span>
|
||||
<span id="cb225-167"><a href="#cb225-167" 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">:<16}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Zielwert'</span><span class="sc">:>14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Aufbau'</span><span class="sc">:>9}</span><span class="ss"> "</span></span>
|
||||
<span id="cb225-168"><a href="#cb225-168" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'Loesen'</span><span class="sc">:>9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Anteil'</span><span class="sc">:>8}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Speicher'</span><span class="sc">:>10}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb225-169"><a href="#cb225-169" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" "</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">72</span>)</span>
|
||||
<span id="cb225-170"><a href="#cb225-170" aria-hidden="true" tabindex="-1"></a> zielwerte <span class="op">=</span> {}</span>
|
||||
<span id="cb225-171"><a href="#cb225-171" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, funktion <span class="kw">in</span> ANSAETZE.items():</span>
|
||||
<span id="cb225-172"><a href="#cb225-172" aria-hidden="true" tabindex="-1"></a> werte, fehler <span class="op">=</span> messe(funktion, m, n)</span>
|
||||
<span id="cb225-173"><a href="#cb225-173" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> werte <span class="kw">is</span> <span class="va">None</span>:</span>
|
||||
<span id="cb225-174"><a href="#cb225-174" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>name<span class="sc">:<16}</span><span class="ss"> nicht verfuegbar: </span><span class="sc">{</span>fehler<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb225-175"><a href="#cb225-175" aria-hidden="true" tabindex="-1"></a> <span class="cf">continue</span></span>
|
||||
<span id="cb225-176"><a href="#cb225-176" aria-hidden="true" tabindex="-1"></a> ziel, aufbau, loesen, speicher <span class="op">=</span> werte</span>
|
||||
<span id="cb225-177"><a href="#cb225-177" aria-hidden="true" tabindex="-1"></a> zielwerte[name] <span class="op">=</span> ziel</span>
|
||||
<span id="cb225-178"><a href="#cb225-178" aria-hidden="true" tabindex="-1"></a> anteil <span class="op">=</span> aufbau <span class="op">/</span> (aufbau <span class="op">+</span> loesen) <span class="op">*</span> <span class="dv">100</span></span>
|
||||
<span id="cb225-179"><a href="#cb225-179" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>name<span class="sc">:<16}</span><span class="ss"> </span><span class="sc">{</span>ziel<span class="sc">:>14,.2f}</span><span class="ss"> </span><span class="sc">{</span>aufbau<span class="sc">:>8.3f}</span><span class="ss">s "</span></span>
|
||||
<span id="cb225-180"><a href="#cb225-180" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>loesen<span class="sc">:>8.3f}</span><span class="ss">s </span><span class="sc">{</span>anteil<span class="sc">:>7.0f}</span><span class="ss">% </span><span class="sc">{</span>speicher<span class="sc">:>9.0f}</span><span class="ss"> MB"</span>)</span>
|
||||
<span id="cb225-181"><a href="#cb225-181" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-182"><a href="#cb225-182" aria-hidden="true" tabindex="-1"></a> <span class="co"># Die wichtigste Zeile: Rechnen alle dasselbe aus?</span></span>
|
||||
<span id="cb225-183"><a href="#cb225-183" aria-hidden="true" tabindex="-1"></a> spanne <span class="op">=</span> <span class="bu">max</span>(zielwerte.values()) <span class="op">-</span> <span class="bu">min</span>(zielwerte.values())</span>
|
||||
<span id="cb225-184"><a href="#cb225-184" aria-hidden="true" tabindex="-1"></a> bezug <span class="op">=</span> <span class="bu">max</span>(<span class="bu">abs</span>(v) <span class="cf">for</span> v <span class="kw">in</span> zielwerte.values())</span>
|
||||
<span id="cb225-185"><a href="#cb225-185" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">''</span><span class="sc">:16}</span><span class="ss"> Spannweite der Zielwerte: </span><span class="sc">{</span>spanne<span class="sc">:.2e}</span><span class="ss"> "</span></span>
|
||||
<span id="cb225-186"><a href="#cb225-186" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"(relativ </span><span class="sc">{</span>spanne <span class="op">/</span> bezug<span class="sc">:.1e}</span><span class="ss">)"</span>)</span>
|
||||
<span id="cb225-187"><a href="#cb225-187" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> spanne <span class="op">/</span> bezug <span class="op">></span> <span class="fl">1e-6</span>:</span>
|
||||
<span id="cb225-188"><a href="#cb225-188" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ACHTUNG: Die Bibliotheken widersprechen sich - "</span></span>
|
||||
<span id="cb225-189"><a href="#cb225-189" aria-hidden="true" tabindex="-1"></a> <span class="st">"der Zeitvergleich ist wertlos."</span>)</span>
|
||||
<span id="cb225-190"><a href="#cb225-190" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb225-191"><a href="#cb225-191" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb225-192"><a href="#cb225-192" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb225-193"><a href="#cb225-193" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" WAS MAN AUS SO EINER TABELLE ABLESEN DARF - UND WAS NICHT"</span>)</span>
|
||||
<span id="cb225-194"><a href="#cb225-194" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb225-195"><a href="#cb225-195" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"DARF man ablesen:"</span>)</span>
|
||||
<span id="cb225-196"><a href="#cb225-196" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Die Spalte 'Anteil' - wie viel der Zeit in den AUFBAU geht statt"</span>)</span>
|
||||
<span id="cb225-197"><a href="#cb225-197" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ins Loesen. Wenn dort 80 </span><span class="sc">% s</span><span class="st">tehen, ist ein schnellerer Solver die"</span>)</span>
|
||||
<span id="cb225-198"><a href="#cb225-198" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" falsche Antwort; dann gehoert das Modell vektorisiert aufgebaut"</span>)</span>
|
||||
<span id="cb225-199"><a href="#cb225-199" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" (Kapitel Oekosystem)."</span>)</span>
|
||||
<span id="cb225-200"><a href="#cb225-200" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Die Groessenordnung des Speicherbedarfs. Sie entscheidet, was auf"</span>)</span>
|
||||
<span id="cb225-201"><a href="#cb225-201" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" einer bestimmten Maschine ueberhaupt laeuft."</span>)</span>
|
||||
<span id="cb225-202"><a href="#cb225-202" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Wie sich beides mit der Groesse ENTWICKELT. Der Trend ist"</span>)</span>
|
||||
<span id="cb225-203"><a href="#cb225-203" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" uebertragbarer als der Absolutwert."</span>)</span>
|
||||
<span id="cb225-204"><a href="#cb225-204" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb225-205"><a href="#cb225-205" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"NICHT ablesen darf man:"</span>)</span>
|
||||
<span id="cb225-206"><a href="#cb225-206" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * 'Bibliothek X ist schneller als Y.' Gemessen wurde EIN"</span>)</span>
|
||||
<span id="cb225-207"><a href="#cb225-207" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Problemtyp in EINER Formulierung. Ein MILP, ein QP oder eine"</span>)</span>
|
||||
<span id="cb225-208"><a href="#cb225-208" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" andere Modellierung desselben Problems koennen die Reihenfolge"</span>)</span>
|
||||
<span id="cb225-209"><a href="#cb225-209" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" umdrehen."</span>)</span>
|
||||
<span id="cb225-210"><a href="#cb225-210" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Etwas ueber Ihre Maschine. Diese Zahlen stammen von einer"</span>)</span>
|
||||
<span id="cb225-211"><a href="#cb225-211" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" anderen. Der Sinn des Programms ist, dass Sie es auf Ihrer"</span>)</span>
|
||||
<span id="cb225-212"><a href="#cb225-212" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" laufen lassen."</span>)</span>
|
||||
<span id="cb225-213"><a href="#cb225-213" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span></code></pre></div>
|
||||
<p><strong>Erwartete Ausgabe</strong> (Zeiten und Speicher hardwareabhängig, die Zielwerte nicht):</p>
|
||||
<pre><code>============================================================================================
|
||||
SKALIERUNGSVERGLEICH: TRANSPORTPROBLEM, VIER BIBLIOTHEKEN
|
||||
|
|
@ -29011,28 +29081,28 @@ die Zielwerte und ihr Verhaeltnis zueinander nicht.
|
|||
--- 10 Lager x 10 Kunden = 100 Variablen ---------------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 13,509.48 0.000s 0.004s 1% 78 MB
|
||||
highspy 13,509.48 0.001s 0.002s 34% 41 MB
|
||||
ortools/GLOP 13,509.48 0.002s 0.001s 80% 54 MB
|
||||
scipy.linprog 13,509.48 0.000s 0.004s 1% 79 MB
|
||||
highspy 13,509.48 0.001s 0.002s 36% 44 MB
|
||||
ortools/GLOP 13,509.48 0.003s 0.001s 80% 56 MB
|
||||
cvxpy 13,509.48 0.001s 0.009s 9% 229 MB
|
||||
Spannweite der Zielwerte: 1.33e-06 (relativ 9.8e-11)
|
||||
|
||||
--- 32 Lager x 32 Kunden = 1,024 Variablen -------------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 35,744.25 0.000s 0.009s 4% 80 MB
|
||||
highspy 35,744.25 0.004s 0.005s 47% 42 MB
|
||||
ortools/GLOP 35,744.25 0.015s 0.002s 85% 55 MB
|
||||
cvxpy 35,744.25 0.001s 0.016s 5% 230 MB
|
||||
scipy.linprog 35,744.25 0.000s 0.009s 3% 81 MB
|
||||
highspy 35,744.25 0.004s 0.005s 44% 45 MB
|
||||
ortools/GLOP 35,744.25 0.014s 0.002s 85% 58 MB
|
||||
cvxpy 35,744.25 0.001s 0.017s 5% 231 MB
|
||||
Spannweite der Zielwerte: 9.54e-05 (relativ 2.7e-09)
|
||||
|
||||
--- 100 Lager x 100 Kunden = 10,000 Variablen ----------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 72,220.34 0.002s 0.070s 3% 120 MB
|
||||
highspy 72,220.34 0.029s 0.034s 45% 47 MB
|
||||
ortools/GLOP 72,220.34 0.123s 0.036s 77% 66 MB
|
||||
cvxpy 72,220.34 0.001s 0.103s 1% 242 MB
|
||||
scipy.linprog 72,220.34 0.004s 0.072s 5% 122 MB
|
||||
highspy 72,220.34 0.026s 0.032s 45% 50 MB
|
||||
ortools/GLOP 72,220.34 0.141s 0.043s 77% 68 MB
|
||||
cvxpy 72,220.34 0.001s 0.108s 1% 242 MB
|
||||
Spannweite der Zielwerte: 3.65e-04 (relativ 5.0e-09)
|
||||
|
||||
============================================================================================
|
||||
|
|
@ -32374,7 +32444,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
|
|||
<h2 id="c9-importfehler">C9 — Importfehler</h2>
|
||||
<pre><code>ImportError: .../highspy/_core...so: undefined symbol: _ZN5Highs13releaseMemoryEv</code></pre>
|
||||
<p><strong>Ursache.</strong> <code>ortools</code> und <code>highspy</code> bringen beide eine eigene HiGHS-Kopie mit; sie lassen sich auf vielen Systemen <strong>nicht im selben Prozess</strong> importieren (siehe <a href="#sec:oekosystem-ein-system-vier-programmieransaetze">Abschnitt 3.5</a>). Der Konflikt entsteht auch <strong>indirekt</strong>: <code>cvxpy</code> importiert ein installiertes <code>highspy</code> bei der Solver-Erkennung selbst mit — ein Skript, das erst <code>cvxpy</code> und dann <code>ortools</code> importiert, crasht daher mit derselben Meldung.</p>
|
||||
<p><strong>Abhilfen (in dieser Reihenfolge):</strong> 1. Nur eines von beiden im selben Skript verwenden. 2. Getrennte Prozesse (<code>subprocess</code>) — siehe <code>Ein_System_Vier_Ansaetze.py</code>. 3. Auf <code>highspy</code> verzichten: HiGHS ist ohnehin Backend von <code>scipy.optimize.linprog</code> und CVXPY. 4. Getrennte virtuelle Umgebungen.</p>
|
||||
<p><strong>Abhilfen (in dieser Reihenfolge):</strong> 1. Nur eines von beiden im selben Skript verwenden. 2. Getrennte Prozesse — ein <code>ProcessPoolExecutor</code> mit <code>mp_context="spawn"</code> und <code>max_tasks_per_child=1</code>, siehe <code>Ein_System_Vier_Ansaetze.py</code>. 3. Auf <code>highspy</code> verzichten: HiGHS ist ohnehin Backend von <code>scipy.optimize.linprog</code> und CVXPY. 4. Getrennte virtuelle Umgebungen.</p>
|
||||
<hr />
|
||||
<h2 id="c10-verdächtig-guter-backtest">C10 — Verdächtig guter Backtest</h2>
|
||||
<p><strong>Faustregel:</strong> Eine Sharpe Ratio über 2 bei einer einfachen Strategie ist fast immer ein Fehler, kein Fund.</p>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">></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">-></span> Loesung:</span>
|
||||
<span id="cb10-245"><a href="#cb10-245" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Startet dieses Programm noch einmal - mit genau einem Solverimport."""</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">-></span> Loesung:</span>
|
||||
<span id="cb10-245"><a href="#cb10-245" aria-hidden="true" tabindex="-1"></a> <span class="co">"""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"> 'spawn' statt des Linux-Standards 'fork': 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">"__main__"</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">></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"> """</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">"spawn"</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">"="</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">" DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE"</span>)</span>
|
||||
<span id="cb10-265"><a href="#cb10-265" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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"Standortplanung: </span><span class="sc">{</span><span class="bu">len</span>(problem.lager)<span class="sc">}</span><span class="ss"> moegliche Lager, "</span></span>
|
||||
<span id="cb10-267"><a href="#cb10-267" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</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."</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"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 "</span></span>
|
||||
<span id="cb10-269"><a href="#cb10-269" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"-> mindestens 3 Lager noetig.</span><span class="ch">\n</span><span class="ss">"</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">"cpsat"</span>, <span class="st">"OR-Tools CP-SAT"</span>),</span>
|
||||
<span id="cb10-273"><a href="#cb10-273" aria-hidden="true" tabindex="-1"></a> (<span class="st">"highs"</span>, <span class="st">"HiGHS (highspy)"</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"</span><span class="sc">{</span>beschriftung<span class="sc">}</span><span class="ss">"</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" </span><span class="sc">{</span>loesung<span class="sc">.</span>als_bericht()<span class="sc">}</span><span class="ss">"</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" eroeffnete Lager: </span><span class="sc">{</span><span class="st">', '</span><span class="sc">.</span>join(geoeffnete_lager(problem, loesung))<span class="sc">}</span><span class="ss">"</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" Abnahmepruefung: "</span></span>
|
||||
<span id="cb10-281"><a href="#cb10-281" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'bestanden'</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">"</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">"-"</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"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"</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">"cpsat"</span>].werte[s]) <span class="op">==</span> <span class="bu">round</span>(loesungen[<span class="st">"highs"</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">"cpsat"</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"Identische Zuordnung: </span><span class="sc">{</span><span class="st">'ja'</span> <span class="cf">if</span> gleich_belegt <span class="cf">else</span> <span class="st">'nein'</span><span class="sc">}</span><span class="ss">"</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"><</span> <span class="fl">0.5</span>, <span class="st">"Die Solver widersprechen sich!"</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">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"="</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">" WAS DER WECHSEL GEKOSTET HAT"</span>)</span>
|
||||
<span id="cb10-298"><a href="#cb10-298" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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">"Ausgetauscht wurde EINE Funktion. Domaenenmodell, Abnahmepruefung und"</span>)</span>
|
||||
<span id="cb10-300"><a href="#cb10-300" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Bericht sind woertlich dieselben - sie sehen den Solver nie."</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">"Nicht umsonst ist der Wechsel trotzdem:"</span>)</span>
|
||||
<span id="cb10-303"><a href="#cb10-303" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * CP-SAT rechnet ausschliesslich GANZZAHLIG. Alle Kosten sind hier"</span>)</span>
|
||||
<span id="cb10-304"><a href="#cb10-304" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" deshalb int. Wer in Euro und Cent rechnet, skaliert vorher auf Cent -"</span>)</span>
|
||||
<span id="cb10-305"><a href="#cb10-305" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" und muss das im Bericht wieder zuruecknehmen."</span>)</span>
|
||||
<span id="cb10-306"><a href="#cb10-306" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * HiGHS braucht die Restriktionen als Matrixzeilen, CP-SAT nimmt sie"</span>)</span>
|
||||
<span id="cb10-307"><a href="#cb10-307" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" als Ausdruecke. Das ist der Grund, warum der HiGHS-Modellbauer"</span>)</span>
|
||||
<span id="cb10-308"><a href="#cb10-308" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" laenger ist, obwohl er dasselbe Modell beschreibt."</span>)</span>
|
||||
<span id="cb10-309"><a href="#cb10-309" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Beide Bibliotheken bringen eine eigene HiGHS-Kopie mit und lassen"</span>)</span>
|
||||
<span id="cb10-310"><a href="#cb10-310" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" sich nicht gemeinsam importieren - daher die zwei Prozesse."</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">"Der Ertrag: Beide beweisen denselben optimalen Zielwert, und die"</span>)</span>
|
||||
<span id="cb10-313"><a href="#cb10-313" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Entscheidung zwischen ihnen ist eine Frage der Laufzeit geworden -"</span>)</span>
|
||||
<span id="cb10-314"><a href="#cb10-314" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"nicht eine Frage, wie viel Code man neu schreiben muss."</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">"__main__"</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">"="</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">" DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE"</span>)</span>
|
||||
<span id="cb10-269"><a href="#cb10-269" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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"Standortplanung: </span><span class="sc">{</span><span class="bu">len</span>(problem.lager)<span class="sc">}</span><span class="ss"> moegliche Lager, "</span></span>
|
||||
<span id="cb10-271"><a href="#cb10-271" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</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."</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"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 "</span></span>
|
||||
<span id="cb10-273"><a href="#cb10-273" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"-> mindestens 3 Lager noetig.</span><span class="ch">\n</span><span class="ss">"</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">"cpsat"</span>, <span class="st">"OR-Tools CP-SAT"</span>),</span>
|
||||
<span id="cb10-277"><a href="#cb10-277" aria-hidden="true" tabindex="-1"></a> (<span class="st">"highs"</span>, <span class="st">"HiGHS (highspy)"</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"</span><span class="sc">{</span>beschriftung<span class="sc">}</span><span class="ss">"</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" </span><span class="sc">{</span>loesung<span class="sc">.</span>als_bericht()<span class="sc">}</span><span class="ss">"</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" eroeffnete Lager: </span><span class="sc">{</span><span class="st">', '</span><span class="sc">.</span>join(geoeffnete_lager(problem, loesung))<span class="sc">}</span><span class="ss">"</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" Abnahmepruefung: "</span></span>
|
||||
<span id="cb10-285"><a href="#cb10-285" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'bestanden'</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">"</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">"-"</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"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"</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">"cpsat"</span>].werte[s]) <span class="op">==</span> <span class="bu">round</span>(loesungen[<span class="st">"highs"</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">"cpsat"</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"Identische Zuordnung: </span><span class="sc">{</span><span class="st">'ja'</span> <span class="cf">if</span> gleich_belegt <span class="cf">else</span> <span class="st">'nein'</span><span class="sc">}</span><span class="ss">"</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"><</span> <span class="fl">0.5</span>, <span class="st">"Die Solver widersprechen sich!"</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">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"="</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">" WAS DER WECHSEL GEKOSTET HAT"</span>)</span>
|
||||
<span id="cb10-302"><a href="#cb10-302" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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">"Ausgetauscht wurde EINE Funktion. Domaenenmodell, Abnahmepruefung und"</span>)</span>
|
||||
<span id="cb10-304"><a href="#cb10-304" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Bericht sind woertlich dieselben - sie sehen den Solver nie."</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">"Nicht umsonst ist der Wechsel trotzdem:"</span>)</span>
|
||||
<span id="cb10-307"><a href="#cb10-307" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * CP-SAT rechnet ausschliesslich GANZZAHLIG. Alle Kosten sind hier"</span>)</span>
|
||||
<span id="cb10-308"><a href="#cb10-308" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" deshalb int. Wer in Euro und Cent rechnet, skaliert vorher auf Cent -"</span>)</span>
|
||||
<span id="cb10-309"><a href="#cb10-309" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" und muss das im Bericht wieder zuruecknehmen."</span>)</span>
|
||||
<span id="cb10-310"><a href="#cb10-310" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * HiGHS braucht die Restriktionen als Matrixzeilen, CP-SAT nimmt sie"</span>)</span>
|
||||
<span id="cb10-311"><a href="#cb10-311" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" als Ausdruecke. Das ist der Grund, warum der HiGHS-Modellbauer"</span>)</span>
|
||||
<span id="cb10-312"><a href="#cb10-312" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" laenger ist, obwohl er dasselbe Modell beschreibt."</span>)</span>
|
||||
<span id="cb10-313"><a href="#cb10-313" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Beide Bibliotheken bringen eine eigene HiGHS-Kopie mit und lassen"</span>)</span>
|
||||
<span id="cb10-314"><a href="#cb10-314" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" sich nicht gemeinsam importieren - daher die zwei Prozesse."</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">"Verglichen wird deshalb der ZIELWERT, nicht der Plan: Gibt es mehrere"</span>)</span>
|
||||
<span id="cb10-317"><a href="#cb10-317" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"gleich teure Loesungen, darf jeder Solver eine andere davon liefern."</span>)</span>
|
||||
<span id="cb10-318"><a href="#cb10-318" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Hier stimmen sie zufaellig ueberein - darauf zu testen waere trotzdem"</span>)</span>
|
||||
<span id="cb10-319"><a href="#cb10-319" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"ein unzuverlaessiger Test (siehe JobShop_Intervalle.py)."</span>)</span>
|
||||
<span id="cb10-320"><a href="#cb10-320" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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">"Der Ertrag: Beide beweisen denselben optimalen Zielwert, und die"</span>)</span>
|
||||
<span id="cb10-317"><a href="#cb10-317" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Entscheidung zwischen ihnen ist eine Frage der Laufzeit geworden -"</span>)</span>
|
||||
<span id="cb10-318"><a href="#cb10-318" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"nicht eine Frage, wie viel Code man neu schreiben muss."</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">"Verglichen wird deshalb der ZIELWERT, nicht der Plan: Gibt es mehrere"</span>)</span>
|
||||
<span id="cb10-321"><a href="#cb10-321" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"gleich teure Loesungen, darf jeder Solver eine andere davon liefern."</span>)</span>
|
||||
<span id="cb10-322"><a href="#cb10-322" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Hier stimmen sie zufaellig ueberein - darauf zu testen waere trotzdem"</span>)</span>
|
||||
<span id="cb10-323"><a href="#cb10-323" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"ein unzuverlaessiger Test (siehe JobShop_Intervalle.py)."</span>)</span>
|
||||
<span id="cb10-324"><a href="#cb10-324" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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
|
||||
|
|
|
|||
|
|
@ -33,113 +33,124 @@ Benoetigt: numpy; in den Kindprozessen scipy, highspy, ortools, cvxpy
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import multiprocessing
|
||||
import resource
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
import numpy as np
|
||||
|
||||
GROESSEN = [(10, 10), (32, 32), (100, 100)] # (Lager, Kunden) -> 100 / 1.024 / 10.000 Variablen
|
||||
|
||||
|
||||
# Jeder Eintrag ist ein eigenstaendiges Programm: Instanz aufbauen, loesen,
|
||||
# Ergebnis als JSON ausgeben. Die Instanz wird in jedem Kindprozess aus
|
||||
# derselben Saat neu erzeugt - so reist nichts ueber die Prozessgrenze,
|
||||
# was das Ergebnis verfaelschen koennte.
|
||||
VORSPANN = """
|
||||
import json, time, resource
|
||||
import numpy as np
|
||||
# Instanz und Speichermessung stehen als gewoehnliche Funktionen hier - nicht
|
||||
# in einem String, den ein Kindprozess ausfuehrt. Jede Messfunktion baut die
|
||||
# Instanz aus derselben Saat neu auf, damit ueber die Prozessgrenze nichts
|
||||
# reist, was das Ergebnis verfaelschen koennte.
|
||||
|
||||
def instanz(m, n):
|
||||
def instanz(m: int, n: int):
|
||||
rng = np.random.default_rng(20)
|
||||
kosten = rng.integers(5, 95, (m, n)).astype(float)
|
||||
angebot = rng.integers(50, 150, m).astype(float)
|
||||
bedarf = angebot.sum() * rng.dirichlet(np.ones(n))
|
||||
return kosten, angebot, bedarf
|
||||
|
||||
def speicher_mb():
|
||||
# ru_maxrss ist unter Linux in Kilobyte
|
||||
|
||||
def speicher_mb() -> float:
|
||||
# ru_maxrss ist unter Linux in Kilobyte. Gemessen wird der Kindprozess -
|
||||
# deshalb muss jede Messung einen eigenen bekommen.
|
||||
return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024
|
||||
|
||||
M, N = {m}, {n}
|
||||
kosten, angebot, bedarf = instanz(M, N)
|
||||
"""
|
||||
|
||||
ANSAETZE = {
|
||||
"scipy.linprog": """
|
||||
def messe_scipy(m: int, n: int):
|
||||
from scipy.optimize import linprog
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
c = kosten.reshape(-1)
|
||||
A_ub = np.zeros((M, M * N)); A_eq = np.zeros((N, M * N))
|
||||
for i in range(M):
|
||||
A_ub[i, i * N:(i + 1) * N] = 1.0
|
||||
for j in range(N):
|
||||
A_eq[j, j::N] = 1.0
|
||||
A_ub = np.zeros((m, m * n)); A_eq = np.zeros((n, m * n))
|
||||
for i in range(m):
|
||||
A_ub[i, i * n:(i + 1) * n] = 1.0
|
||||
for j in range(n):
|
||||
A_eq[j, j::n] = 1.0
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter()
|
||||
r = linprog(c=c, A_ub=A_ub, b_ub=angebot, A_eq=A_eq, b_eq=bedarf,
|
||||
bounds=(0, None), method="highs")
|
||||
loesen = time.perf_counter() - t0
|
||||
ausgabe = (float(r.fun), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return float(r.fun), aufbau, loesen, speicher_mb()
|
||||
|
||||
"highspy": """
|
||||
|
||||
def messe_highspy(m: int, n: int):
|
||||
import highspy
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
h = highspy.Highs(); h.setOptionValue("output_flag", False)
|
||||
h.addVars(M * N, np.zeros(M * N), np.full(M * N, highspy.kHighsInf))
|
||||
for k in range(M * N):
|
||||
h.addVars(m * n, np.zeros(m * n), np.full(m * n, highspy.kHighsInf))
|
||||
for k in range(m * n):
|
||||
h.changeColCost(k, float(kosten.reshape(-1)[k]))
|
||||
for i in range(M):
|
||||
idx = np.arange(i * N, (i + 1) * N, dtype=np.int32)
|
||||
h.addRow(-highspy.kHighsInf, float(angebot[i]), N, idx, np.ones(N))
|
||||
for j in range(N):
|
||||
idx = np.arange(j, M * N, N, dtype=np.int32)
|
||||
h.addRow(float(bedarf[j]), float(bedarf[j]), M, idx, np.ones(M))
|
||||
for i in range(m):
|
||||
idx = np.arange(i * n, (i + 1) * n, dtype=np.int32)
|
||||
h.addRow(-highspy.kHighsInf, float(angebot[i]), n, idx, np.ones(n))
|
||||
for j in range(n):
|
||||
idx = np.arange(j, m * n, n, dtype=np.int32)
|
||||
h.addRow(float(bedarf[j]), float(bedarf[j]), m, idx, np.ones(m))
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); h.run(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (h.getInfo().objective_function_value, aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return h.getInfo().objective_function_value, aufbau, loesen, speicher_mb()
|
||||
|
||||
"ortools/GLOP": """
|
||||
|
||||
def messe_ortools(m: int, n: int):
|
||||
from ortools.linear_solver import pywraplp
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
s = pywraplp.Solver.CreateSolver("GLOP")
|
||||
x = [[s.NumVar(0, s.infinity(), f"x{i}_{j}") for j in range(N)]
|
||||
for i in range(M)]
|
||||
for i in range(M):
|
||||
x = [[s.NumVar(0, s.infinity(), f"x{i}_{j}") for j in range(n)]
|
||||
for i in range(m)]
|
||||
for i in range(m):
|
||||
s.Add(sum(x[i]) <= float(angebot[i]))
|
||||
for j in range(N):
|
||||
s.Add(sum(x[i][j] for i in range(M)) == float(bedarf[j]))
|
||||
for j in range(n):
|
||||
s.Add(sum(x[i][j] for i in range(m)) == float(bedarf[j]))
|
||||
s.Minimize(sum(float(kosten[i, j]) * x[i][j]
|
||||
for i in range(M) for j in range(N)))
|
||||
for i in range(m) for j in range(n)))
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); s.Solve(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (s.Objective().Value(), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return s.Objective().Value(), aufbau, loesen, speicher_mb()
|
||||
|
||||
"cvxpy": """
|
||||
|
||||
def messe_cvxpy(m: int, n: int):
|
||||
import cvxpy as cp
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
x = cp.Variable((M, N), nonneg=True)
|
||||
x = cp.Variable((m, n), nonneg=True)
|
||||
problem = cp.Problem(cp.Minimize(cp.sum(cp.multiply(kosten, x))),
|
||||
[cp.sum(x, axis=1) <= angebot,
|
||||
cp.sum(x, axis=0) == bedarf])
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); problem.solve(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (float(problem.value), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
}
|
||||
return float(problem.value), aufbau, loesen, speicher_mb()
|
||||
|
||||
|
||||
def messe(name: str, quelltext: str, m: int, n: int):
|
||||
"""Fuehrt einen Ansatz in einem eigenen Prozess aus."""
|
||||
programm = (VORSPANN.format(m=m, n=n) + textwrap.dedent(quelltext)
|
||||
+ "\nprint(json.dumps(ausgabe))\n")
|
||||
ergebnis = subprocess.run([sys.executable, "-c", programm],
|
||||
capture_output=True, text=True, timeout=600)
|
||||
if ergebnis.returncode != 0:
|
||||
return None, ergebnis.stderr.strip().splitlines()[-1][:60]
|
||||
return json.loads(ergebnis.stdout.strip().splitlines()[-1]), None
|
||||
ANSAETZE = {"scipy.linprog": messe_scipy, "highspy": messe_highspy,
|
||||
"ortools/GLOP": messe_ortools, "cvxpy": messe_cvxpy}
|
||||
|
||||
|
||||
def messe(funktion, m: int, n: int):
|
||||
"""Fuehrt eine Messfunktion in einem FRISCHEN Prozess aus.
|
||||
|
||||
'spawn' und max_tasks_per_child=1 zusammen garantieren, was Regel 1
|
||||
verlangt: Jede Messung sieht einen leeren Interpreter. Ohne das
|
||||
zweite wuerde der Pool seinen Arbeiter wiederverwenden - dann waere
|
||||
der Speicherwert der zweiten Bibliothek um die erste zu hoch, und
|
||||
ortools und highspy saessen im selben Prozess.
|
||||
"""
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
try:
|
||||
return pool.submit(funktion, m, n).result(timeout=600), None
|
||||
except Exception as fehler:
|
||||
return None, str(fehler).strip().splitlines()[-1][:60]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
@ -157,8 +168,8 @@ if __name__ == "__main__":
|
|||
f"{'Loesen':>9} {'Anteil':>8} {'Speicher':>10}")
|
||||
print(" " + "-" * 72)
|
||||
zielwerte = {}
|
||||
for name, quelltext in ANSAETZE.items():
|
||||
werte, fehler = messe(name, quelltext, m, n)
|
||||
for name, funktion in ANSAETZE.items():
|
||||
werte, fehler = messe(funktion, m, n)
|
||||
if werte is None:
|
||||
print(f" {name:<16} nicht verfuegbar: {fehler}")
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -8,85 +8,104 @@ Kapitel Oekosystem: Dasselbe LP in vier Bibliotheken.
|
|||
2*x1 + 3*x2 + x3 <= 50
|
||||
x >= 0
|
||||
|
||||
Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich am Ende.
|
||||
Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich
|
||||
am Ende.
|
||||
|
||||
WICHTIG: Jeder Solver läuft in einem EIGENEN Prozess, weil sich ortools und
|
||||
WICHTIG: Jeder Solver laeuft in einem EIGENEN Prozess, weil sich ortools und
|
||||
highspy auf vielen Systemen nicht gemeinsam importieren lassen (beide bringen
|
||||
eine eigene HiGHS-Kopie mit -> Symbolkonflikt).
|
||||
|
||||
Die Isolation besorgt ein ProcessPoolExecutor. Drei Einstellungen ergeben
|
||||
zusammen die Garantie:
|
||||
|
||||
mp_context "spawn" Der Kindprozess startet mit einem FRISCHEN
|
||||
Interpreter, statt den Speicher des Elternprozesses
|
||||
zu erben. Was hier schon importiert ist, ist dort
|
||||
nicht importiert. Mit dem Standard "fork" auf Linux
|
||||
waere das nicht so.
|
||||
max_tasks_per_child=1 Jede Aufgabe bekommt einen NEUEN Prozess. Ohne das
|
||||
wuerde der Pool seinen Arbeiter wiederverwenden - und
|
||||
beim zweiten Solver waere der Konflikt zurueck.
|
||||
max_workers=1 Haelt die vier Laeufe nacheinander. Nicht aus
|
||||
Vorsicht, sondern damit die gemessenen Zeiten
|
||||
vergleichbar bleiben.
|
||||
|
||||
Jeder Solver steht in einer eigenen Funktion mit LOKALEM Import. Das ist der
|
||||
Unterschied zu einem Codestring, den man an 'python -c' uebergibt: Die
|
||||
Funktion laesst sich einzeln aufrufen, testen und vom Editor pruefen - ein
|
||||
String nicht.
|
||||
|
||||
Benoetigt: scipy, highspy, cvxpy, ortools
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import multiprocessing
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
ERWARTET = 530.0 # Ergebnis der Handrechnung zum Produktionsprogramm
|
||||
|
||||
# Jeder Eintrag ist ein eigenständiges Miniprogramm, das sein Ergebnis als
|
||||
# JSON auf stdout ausgibt. So bleibt jeder Import in seinem eigenen Prozess.
|
||||
ANSAETZE: dict[str, str] = {
|
||||
# Die Instanz - einmal notiert, von allen vier Funktionen benutzt.
|
||||
ZIEL = [10.0, 15.0, 25.0]
|
||||
MATRIX = [[1, 1, 2], [2, 3, 1]]
|
||||
KAPAZITAET = [40.0, 50.0]
|
||||
|
||||
"scipy.optimize.linprog": """
|
||||
|
||||
def loese_mit_scipy() -> tuple[float, list[float]]:
|
||||
from scipy.optimize import linprog
|
||||
res = linprog(c=[-10.0, -15.0, -25.0], # linprog MINIMIERT -> negieren
|
||||
A_ub=[[1, 1, 2], [2, 3, 1]], b_ub=[40, 50],
|
||||
ergebnis = linprog(c=[-w for w in ZIEL], # linprog MINIMIERT -> negieren
|
||||
A_ub=MATRIX, b_ub=KAPAZITAET,
|
||||
bounds=[(0, None)] * 3, method="highs")
|
||||
ausgabe = (-res.fun, list(res.x))
|
||||
""",
|
||||
return -ergebnis.fun, list(ergebnis.x)
|
||||
|
||||
"highspy (natives HiGHS)": """
|
||||
import numpy as np, highspy
|
||||
|
||||
def loese_mit_highspy() -> tuple[float, list[float]]:
|
||||
import highspy
|
||||
import numpy as np
|
||||
h = highspy.Highs()
|
||||
h.setOptionValue("output_flag", False)
|
||||
h.addVars(3, np.zeros(3), np.full(3, highspy.kHighsInf))
|
||||
h.changeObjectiveSense(highspy.ObjSense.kMaximize)
|
||||
for j, wert in enumerate([10.0, 15.0, 25.0]):
|
||||
for j, wert in enumerate(ZIEL):
|
||||
h.changeColCost(j, wert)
|
||||
# CSR-Format: starts[i] = Beginn von Zeile i in indices/values
|
||||
h.addRows(2, np.full(2, -highspy.kHighsInf), np.array([40.0, 50.0]), 6,
|
||||
h.addRows(2, np.full(2, -highspy.kHighsInf), np.array(KAPAZITAET), 6,
|
||||
np.array([0, 3], dtype=np.int32),
|
||||
np.array([0, 1, 2, 0, 1, 2], dtype=np.int32),
|
||||
np.array([1.0, 1.0, 2.0, 2.0, 3.0, 1.0]))
|
||||
np.array([float(w) for zeile in MATRIX for w in zeile]))
|
||||
h.run()
|
||||
ausgabe = (h.getInfo().objective_function_value,
|
||||
return (h.getInfo().objective_function_value,
|
||||
list(h.getSolution().col_value[:3]))
|
||||
""",
|
||||
|
||||
"cvxpy": """
|
||||
import numpy as np, cvxpy as cp
|
||||
|
||||
def loese_mit_cvxpy() -> tuple[float, list[float]]:
|
||||
import cvxpy as cp
|
||||
import numpy as np
|
||||
x = cp.Variable(3, nonneg=True)
|
||||
problem = cp.Problem(cp.Maximize(np.array([10.0, 15.0, 25.0]) @ x),
|
||||
[np.array([[1, 1, 2], [2, 3, 1]]) @ x <= np.array([40, 50])])
|
||||
problem = cp.Problem(cp.Maximize(np.array(ZIEL) @ x),
|
||||
[np.array(MATRIX) @ x <= np.array(KAPAZITAET)])
|
||||
problem.solve()
|
||||
ausgabe = (float(problem.value), [float(v) for v in x.value])
|
||||
""",
|
||||
return float(problem.value), [float(v) for v in x.value]
|
||||
|
||||
"ortools / GLOP": """
|
||||
|
||||
def loese_mit_ortools() -> tuple[float, list[float]]:
|
||||
from ortools.linear_solver import pywraplp
|
||||
s = pywraplp.Solver.CreateSolver("GLOP")
|
||||
x = [s.NumVar(0, s.infinity(), f"x{j+1}") for j in range(3)]
|
||||
A = [[1, 1, 2], [2, 3, 1]]
|
||||
for i, kap in enumerate([40, 50]):
|
||||
s.Add(sum(A[i][j] * x[j] for j in range(3)) <= kap)
|
||||
s.Maximize(10 * x[0] + 15 * x[1] + 25 * x[2])
|
||||
for i, kapazitaet in enumerate(KAPAZITAET):
|
||||
s.Add(sum(MATRIX[i][j] * x[j] for j in range(3)) <= kapazitaet)
|
||||
s.Maximize(sum(ZIEL[j] * x[j] for j in range(3)))
|
||||
s.Solve()
|
||||
ausgabe = (s.Objective().Value(), [v.solution_value() for v in x])
|
||||
""",
|
||||
return s.Objective().Value(), [v.solution_value() for v in x]
|
||||
|
||||
|
||||
ANSAETZE = {
|
||||
"scipy.optimize.linprog": loese_mit_scipy,
|
||||
"highspy (natives HiGHS)": loese_mit_highspy,
|
||||
"cvxpy": loese_mit_cvxpy,
|
||||
"ortools / GLOP": loese_mit_ortools,
|
||||
}
|
||||
|
||||
|
||||
def fuehre_in_eigenem_prozess_aus(quelltext: str) -> tuple[float, list[float]]:
|
||||
"""Startet den Codeschnipsel als separaten Python-Prozess und liest das Ergebnis."""
|
||||
programm = textwrap.dedent(quelltext) + "\nimport json; print(json.dumps(ausgabe))\n"
|
||||
ergebnis = subprocess.run([sys.executable, "-c", programm],
|
||||
capture_output=True, text=True, timeout=120)
|
||||
if ergebnis.returncode != 0:
|
||||
raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])
|
||||
wert, loesung = json.loads(ergebnis.stdout.strip().splitlines()[-1])
|
||||
return wert, loesung
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 78)
|
||||
print(" EIN SYSTEM - VIER ANSAETZE (je eigener Prozess)")
|
||||
|
|
@ -95,14 +114,20 @@ if __name__ == "__main__":
|
|||
print("-" * 78)
|
||||
|
||||
werte = []
|
||||
for name, quelltext in ANSAETZE.items():
|
||||
t0 = time.perf_counter()
|
||||
# Ein Pool, vier Aufgaben, vier frische Prozesse. Der Kontext muss
|
||||
# "spawn" sein - siehe Modulkommentar.
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
for name, funktion in ANSAETZE.items():
|
||||
beginn = time.perf_counter()
|
||||
try:
|
||||
wert, x = fuehre_in_eigenem_prozess_aus(quelltext)
|
||||
except RuntimeError as fehler:
|
||||
print(f"{name:<26} nicht verfuegbar: {fehler[:40]}")
|
||||
wert, x = pool.submit(funktion).result(timeout=120)
|
||||
except Exception as fehler: # Bibliothek fehlt o. Ae.
|
||||
print(f"{name:<26} nicht verfuegbar: {str(fehler)[:40]}")
|
||||
continue
|
||||
dauer = time.perf_counter() - t0
|
||||
dauer = time.perf_counter() - beginn
|
||||
werte.append(wert)
|
||||
print(f"{name:<26} {wert:>10.2f} {x[0]:>7.2f} {x[1]:>7.2f} {x[2]:>7.2f} "
|
||||
f"{dauer:>8.2f} s")
|
||||
|
|
@ -115,6 +140,6 @@ if __name__ == "__main__":
|
|||
assert spanne < 1e-6, "Die Bibliotheken widersprechen sich!"
|
||||
assert abs(werte[0] - ERWARTET) < 1e-6, "Ergebnis weicht von der Handrechnung ab!"
|
||||
print("Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.")
|
||||
print("(Die Zeiten enthalten den Prozessstart und den Import - sie messen")
|
||||
print(" NICHT die reine Solverleistung, siehe Uebung 3.5.)")
|
||||
print("(Die Zeiten enthalten Prozessstart und Import - sie messen NICHT die")
|
||||
print(" reine Solverleistung. Die Uebungsaufgabe 'Laufzeitvergleich' trennt beides.)")
|
||||
print("=" * 78)
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ Benoetigt: numpy, pydantic, ortools, highspy (jeweils im eigenen Prozess)
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import multiprocessing
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
import numpy as np
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
|
@ -241,25 +241,29 @@ def geoeffnete_lager(problem: Standortproblem, loesung: Loesung) -> list[str]:
|
|||
if any(loesung.werte[problem.schluessel(i, j)] > 0.5 for j in range(m))]
|
||||
|
||||
|
||||
def loese_in_eigenem_prozess(name: str) -> Loesung:
|
||||
"""Startet dieses Programm noch einmal - mit genau einem Solverimport."""
|
||||
ergebnis = subprocess.run([sys.executable, __file__, name],
|
||||
capture_output=True, text=True, timeout=300)
|
||||
if ergebnis.returncode != 0:
|
||||
raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])
|
||||
# Das DTO als JSON - genau dafuer ist ein Datenobjekt ohne Solverbezug gut.
|
||||
return Loesung.model_validate_json(ergebnis.stdout.strip().splitlines()[-1])
|
||||
def loese_in_eigenem_prozess(name: str, problem: Standortproblem) -> Loesung:
|
||||
"""Laesst genau einen Modellbauer in einem frischen Prozess rechnen.
|
||||
|
||||
'spawn' statt des Linux-Standards 'fork': Der Kindprozess startet mit
|
||||
einem leeren Interpreter und importiert nur den Solver, den SEIN
|
||||
Modellbauer braucht. max_tasks_per_child=1 sorgt dafuer, dass der Pool
|
||||
seinen Arbeiter nicht wiederverwendet - sonst saessen beim zweiten Aufruf
|
||||
wieder beide Bibliotheken im selben Prozess.
|
||||
|
||||
Hin und zurueck wandert das Domaenenmodell bzw. das Loesungs-DTO. Beide
|
||||
kennen keinen Solver, sind also serialisierbar - genau dafuer sind sie da.
|
||||
"""
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
return pool.submit(MODELLBAUER[name], problem).result(timeout=300)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
problem = beispielproblem()
|
||||
|
||||
# --- Kindprozess: rechnen und das DTO als JSON ausgeben ---------------
|
||||
if len(sys.argv) > 1:
|
||||
print(MODELLBAUER[sys.argv[1]](problem).model_dump_json())
|
||||
sys.exit(0)
|
||||
|
||||
# --- Hauptprozess: beide Solver anstossen und vergleichen -------------
|
||||
# --- Beide Solver anstossen und vergleichen ---------------------------
|
||||
print("=" * 82)
|
||||
print(" DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE")
|
||||
print("=" * 82)
|
||||
|
|
@ -271,7 +275,7 @@ if __name__ == "__main__":
|
|||
loesungen: dict[str, Loesung] = {}
|
||||
for name, beschriftung in [("cpsat", "OR-Tools CP-SAT"),
|
||||
("highs", "HiGHS (highspy)")]:
|
||||
loesung = loesungen[name] = loese_in_eigenem_prozess(name)
|
||||
loesung = loesungen[name] = loese_in_eigenem_prozess(name, problem)
|
||||
beanstandungen = pruefe_zuordnung(problem, loesung)
|
||||
|
||||
print(f"{beschriftung}")
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -865,173 +865,184 @@ moeglichen Fehler. Was zaehlt, ist die LISTE der Ueberlebenden.
|
|||
<span id="cb9-33"><a href="#cb9-33" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-34"><a href="#cb9-34" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
|
||||
<span id="cb9-35"><a href="#cb9-35" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-36"><a href="#cb9-36" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> json</span>
|
||||
<span id="cb9-37"><a href="#cb9-37" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> subprocess</span>
|
||||
<span id="cb9-38"><a href="#cb9-38" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
|
||||
<span id="cb9-39"><a href="#cb9-39" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> textwrap</span>
|
||||
<span id="cb9-36"><a href="#cb9-36" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> multiprocessing</span>
|
||||
<span id="cb9-37"><a href="#cb9-37" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> resource</span>
|
||||
<span id="cb9-38"><a href="#cb9-38" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
|
||||
<span id="cb9-39"><a href="#cb9-39" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> concurrent.futures <span class="im">import</span> ProcessPoolExecutor</span>
|
||||
<span id="cb9-40"><a href="#cb9-40" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-41"><a href="#cb9-41" aria-hidden="true" tabindex="-1"></a>GROESSEN <span class="op">=</span> [(<span class="dv">10</span>, <span class="dv">10</span>), (<span class="dv">32</span>, <span class="dv">32</span>), (<span class="dv">100</span>, <span class="dv">100</span>)] <span class="co"># (Lager, Kunden) -> 100 / 1.024 / 10.000 Variablen</span></span>
|
||||
<span id="cb9-41"><a href="#cb9-41" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
|
||||
<span id="cb9-42"><a href="#cb9-42" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-43"><a href="#cb9-43" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-44"><a href="#cb9-44" aria-hidden="true" tabindex="-1"></a><span class="co"># Jeder Eintrag ist ein eigenstaendiges Programm: Instanz aufbauen, loesen,</span></span>
|
||||
<span id="cb9-45"><a href="#cb9-45" aria-hidden="true" tabindex="-1"></a><span class="co"># Ergebnis als JSON ausgeben. Die Instanz wird in jedem Kindprozess aus</span></span>
|
||||
<span id="cb9-46"><a href="#cb9-46" aria-hidden="true" tabindex="-1"></a><span class="co"># derselben Saat neu erzeugt - so reist nichts ueber die Prozessgrenze,</span></span>
|
||||
<span id="cb9-47"><a href="#cb9-47" aria-hidden="true" tabindex="-1"></a><span class="co"># was das Ergebnis verfaelschen koennte.</span></span>
|
||||
<span id="cb9-48"><a href="#cb9-48" aria-hidden="true" tabindex="-1"></a>VORSPANN <span class="op">=</span> <span class="st">"""</span></span>
|
||||
<span id="cb9-49"><a href="#cb9-49" aria-hidden="true" tabindex="-1"></a><span class="st">import json, time, resource</span></span>
|
||||
<span id="cb9-50"><a href="#cb9-50" aria-hidden="true" tabindex="-1"></a><span class="st">import numpy as np</span></span>
|
||||
<span id="cb9-51"><a href="#cb9-51" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-52"><a href="#cb9-52" aria-hidden="true" tabindex="-1"></a><span class="st">def instanz(m, n):</span></span>
|
||||
<span id="cb9-53"><a href="#cb9-53" aria-hidden="true" tabindex="-1"></a><span class="st"> rng = np.random.default_rng(20)</span></span>
|
||||
<span id="cb9-54"><a href="#cb9-54" aria-hidden="true" tabindex="-1"></a><span class="st"> kosten = rng.integers(5, 95, (m, n)).astype(float)</span></span>
|
||||
<span id="cb9-55"><a href="#cb9-55" aria-hidden="true" tabindex="-1"></a><span class="st"> angebot = rng.integers(50, 150, m).astype(float)</span></span>
|
||||
<span id="cb9-56"><a href="#cb9-56" aria-hidden="true" tabindex="-1"></a><span class="st"> bedarf = angebot.sum() * rng.dirichlet(np.ones(n))</span></span>
|
||||
<span id="cb9-57"><a href="#cb9-57" aria-hidden="true" tabindex="-1"></a><span class="st"> return kosten, angebot, bedarf</span></span>
|
||||
<span id="cb9-43"><a href="#cb9-43" aria-hidden="true" tabindex="-1"></a>GROESSEN <span class="op">=</span> [(<span class="dv">10</span>, <span class="dv">10</span>), (<span class="dv">32</span>, <span class="dv">32</span>), (<span class="dv">100</span>, <span class="dv">100</span>)] <span class="co"># (Lager, Kunden) -> 100 / 1.024 / 10.000 Variablen</span></span>
|
||||
<span id="cb9-44"><a href="#cb9-44" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-45"><a href="#cb9-45" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-46"><a href="#cb9-46" aria-hidden="true" tabindex="-1"></a><span class="co"># Instanz und Speichermessung stehen als gewoehnliche Funktionen hier - nicht</span></span>
|
||||
<span id="cb9-47"><a href="#cb9-47" aria-hidden="true" tabindex="-1"></a><span class="co"># in einem String, den ein Kindprozess ausfuehrt. Jede Messfunktion baut die</span></span>
|
||||
<span id="cb9-48"><a href="#cb9-48" aria-hidden="true" tabindex="-1"></a><span class="co"># Instanz aus derselben Saat neu auf, damit ueber die Prozessgrenze nichts</span></span>
|
||||
<span id="cb9-49"><a href="#cb9-49" aria-hidden="true" tabindex="-1"></a><span class="co"># reist, was das Ergebnis verfaelschen koennte.</span></span>
|
||||
<span id="cb9-50"><a href="#cb9-50" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-51"><a href="#cb9-51" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> instanz(m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb9-52"><a href="#cb9-52" aria-hidden="true" tabindex="-1"></a> rng <span class="op">=</span> np.random.default_rng(<span class="dv">20</span>)</span>
|
||||
<span id="cb9-53"><a href="#cb9-53" aria-hidden="true" tabindex="-1"></a> kosten <span class="op">=</span> rng.integers(<span class="dv">5</span>, <span class="dv">95</span>, (m, n)).astype(<span class="bu">float</span>)</span>
|
||||
<span id="cb9-54"><a href="#cb9-54" aria-hidden="true" tabindex="-1"></a> angebot <span class="op">=</span> rng.integers(<span class="dv">50</span>, <span class="dv">150</span>, m).astype(<span class="bu">float</span>)</span>
|
||||
<span id="cb9-55"><a href="#cb9-55" aria-hidden="true" tabindex="-1"></a> bedarf <span class="op">=</span> angebot.<span class="bu">sum</span>() <span class="op">*</span> rng.dirichlet(np.ones(n))</span>
|
||||
<span id="cb9-56"><a href="#cb9-56" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> kosten, angebot, bedarf</span>
|
||||
<span id="cb9-57"><a href="#cb9-57" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-58"><a href="#cb9-58" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-59"><a href="#cb9-59" aria-hidden="true" tabindex="-1"></a><span class="st">def speicher_mb():</span></span>
|
||||
<span id="cb9-60"><a href="#cb9-60" aria-hidden="true" tabindex="-1"></a><span class="st"> # ru_maxrss ist unter Linux in Kilobyte</span></span>
|
||||
<span id="cb9-61"><a href="#cb9-61" aria-hidden="true" tabindex="-1"></a><span class="st"> return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024</span></span>
|
||||
<span id="cb9-62"><a href="#cb9-62" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-63"><a href="#cb9-63" aria-hidden="true" tabindex="-1"></a><span class="st">M, N = </span><span class="sc">{m}</span><span class="st">, </span><span class="sc">{n}</span></span>
|
||||
<span id="cb9-64"><a href="#cb9-64" aria-hidden="true" tabindex="-1"></a><span class="st">kosten, angebot, bedarf = instanz(M, N)</span></span>
|
||||
<span id="cb9-65"><a href="#cb9-65" aria-hidden="true" tabindex="-1"></a><span class="st">"""</span></span>
|
||||
<span id="cb9-66"><a href="#cb9-66" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-67"><a href="#cb9-67" aria-hidden="true" tabindex="-1"></a>ANSAETZE <span class="op">=</span> {</span>
|
||||
<span id="cb9-68"><a href="#cb9-68" aria-hidden="true" tabindex="-1"></a> <span class="st">"scipy.linprog"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb9-69"><a href="#cb9-69" aria-hidden="true" tabindex="-1"></a><span class="st"> from scipy.optimize import linprog</span></span>
|
||||
<span id="cb9-70"><a href="#cb9-70" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter()</span></span>
|
||||
<span id="cb9-71"><a href="#cb9-71" aria-hidden="true" tabindex="-1"></a><span class="st"> c = kosten.reshape(-1)</span></span>
|
||||
<span id="cb9-72"><a href="#cb9-72" aria-hidden="true" tabindex="-1"></a><span class="st"> A_ub = np.zeros((M, M * N)); A_eq = np.zeros((N, M * N))</span></span>
|
||||
<span id="cb9-73"><a href="#cb9-73" aria-hidden="true" tabindex="-1"></a><span class="st"> for i in range(M):</span></span>
|
||||
<span id="cb9-74"><a href="#cb9-74" aria-hidden="true" tabindex="-1"></a><span class="st"> A_ub[i, i * N:(i + 1) * N] = 1.0</span></span>
|
||||
<span id="cb9-75"><a href="#cb9-75" aria-hidden="true" tabindex="-1"></a><span class="st"> for j in range(N):</span></span>
|
||||
<span id="cb9-76"><a href="#cb9-76" aria-hidden="true" tabindex="-1"></a><span class="st"> A_eq[j, j::N] = 1.0</span></span>
|
||||
<span id="cb9-77"><a href="#cb9-77" aria-hidden="true" tabindex="-1"></a><span class="st"> aufbau = time.perf_counter() - t0</span></span>
|
||||
<span id="cb9-78"><a href="#cb9-78" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter()</span></span>
|
||||
<span id="cb9-79"><a href="#cb9-79" aria-hidden="true" tabindex="-1"></a><span class="st"> r = linprog(c=c, A_ub=A_ub, b_ub=angebot, A_eq=A_eq, b_eq=bedarf,</span></span>
|
||||
<span id="cb9-80"><a href="#cb9-80" aria-hidden="true" tabindex="-1"></a><span class="st"> bounds=(0, None), method="highs")</span></span>
|
||||
<span id="cb9-81"><a href="#cb9-81" aria-hidden="true" tabindex="-1"></a><span class="st"> loesen = time.perf_counter() - t0</span></span>
|
||||
<span id="cb9-82"><a href="#cb9-82" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (float(r.fun), aufbau, loesen, speicher_mb())</span></span>
|
||||
<span id="cb9-83"><a href="#cb9-83" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb9-84"><a href="#cb9-84" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-85"><a href="#cb9-85" aria-hidden="true" tabindex="-1"></a> <span class="st">"highspy"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb9-86"><a href="#cb9-86" aria-hidden="true" tabindex="-1"></a><span class="st"> import highspy</span></span>
|
||||
<span id="cb9-87"><a href="#cb9-87" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter()</span></span>
|
||||
<span id="cb9-88"><a href="#cb9-88" aria-hidden="true" tabindex="-1"></a><span class="st"> h = highspy.Highs(); h.setOptionValue("output_flag", False)</span></span>
|
||||
<span id="cb9-89"><a href="#cb9-89" aria-hidden="true" tabindex="-1"></a><span class="st"> h.addVars(M * N, np.zeros(M * N), np.full(M * N, highspy.kHighsInf))</span></span>
|
||||
<span id="cb9-90"><a href="#cb9-90" aria-hidden="true" tabindex="-1"></a><span class="st"> for k in range(M * N):</span></span>
|
||||
<span id="cb9-91"><a href="#cb9-91" aria-hidden="true" tabindex="-1"></a><span class="st"> h.changeColCost(k, float(kosten.reshape(-1)[k]))</span></span>
|
||||
<span id="cb9-92"><a href="#cb9-92" aria-hidden="true" tabindex="-1"></a><span class="st"> for i in range(M):</span></span>
|
||||
<span id="cb9-93"><a href="#cb9-93" aria-hidden="true" tabindex="-1"></a><span class="st"> idx = np.arange(i * N, (i + 1) * N, dtype=np.int32)</span></span>
|
||||
<span id="cb9-94"><a href="#cb9-94" aria-hidden="true" tabindex="-1"></a><span class="st"> h.addRow(-highspy.kHighsInf, float(angebot[i]), N, idx, np.ones(N))</span></span>
|
||||
<span id="cb9-95"><a href="#cb9-95" aria-hidden="true" tabindex="-1"></a><span class="st"> for j in range(N):</span></span>
|
||||
<span id="cb9-96"><a href="#cb9-96" aria-hidden="true" tabindex="-1"></a><span class="st"> idx = np.arange(j, M * N, N, dtype=np.int32)</span></span>
|
||||
<span id="cb9-97"><a href="#cb9-97" aria-hidden="true" tabindex="-1"></a><span class="st"> h.addRow(float(bedarf[j]), float(bedarf[j]), M, idx, np.ones(M))</span></span>
|
||||
<span id="cb9-98"><a href="#cb9-98" aria-hidden="true" tabindex="-1"></a><span class="st"> aufbau = time.perf_counter() - t0</span></span>
|
||||
<span id="cb9-99"><a href="#cb9-99" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter(); h.run(); loesen = time.perf_counter() - t0</span></span>
|
||||
<span id="cb9-100"><a href="#cb9-100" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (h.getInfo().objective_function_value, aufbau, loesen, speicher_mb())</span></span>
|
||||
<span id="cb9-101"><a href="#cb9-101" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb9-102"><a href="#cb9-102" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-103"><a href="#cb9-103" aria-hidden="true" tabindex="-1"></a> <span class="st">"ortools/GLOP"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb9-104"><a href="#cb9-104" aria-hidden="true" tabindex="-1"></a><span class="st"> from ortools.linear_solver import pywraplp</span></span>
|
||||
<span id="cb9-105"><a href="#cb9-105" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter()</span></span>
|
||||
<span id="cb9-106"><a href="#cb9-106" aria-hidden="true" tabindex="-1"></a><span class="st"> s = pywraplp.Solver.CreateSolver("GLOP")</span></span>
|
||||
<span id="cb9-107"><a href="#cb9-107" aria-hidden="true" tabindex="-1"></a><span class="st"> x = [[s.NumVar(0, s.infinity(), f"x</span><span class="sc">{i}</span><span class="st">_</span><span class="sc">{j}</span><span class="st">") for j in range(N)]</span></span>
|
||||
<span id="cb9-108"><a href="#cb9-108" aria-hidden="true" tabindex="-1"></a><span class="st"> for i in range(M)]</span></span>
|
||||
<span id="cb9-109"><a href="#cb9-109" aria-hidden="true" tabindex="-1"></a><span class="st"> for i in range(M):</span></span>
|
||||
<span id="cb9-110"><a href="#cb9-110" aria-hidden="true" tabindex="-1"></a><span class="st"> s.Add(sum(x[i]) <= float(angebot[i]))</span></span>
|
||||
<span id="cb9-111"><a href="#cb9-111" aria-hidden="true" tabindex="-1"></a><span class="st"> for j in range(N):</span></span>
|
||||
<span id="cb9-112"><a href="#cb9-112" aria-hidden="true" tabindex="-1"></a><span class="st"> s.Add(sum(x[i][j] for i in range(M)) == float(bedarf[j]))</span></span>
|
||||
<span id="cb9-113"><a href="#cb9-113" aria-hidden="true" tabindex="-1"></a><span class="st"> s.Minimize(sum(float(kosten[i, j]) * x[i][j]</span></span>
|
||||
<span id="cb9-114"><a href="#cb9-114" aria-hidden="true" tabindex="-1"></a><span class="st"> for i in range(M) for j in range(N)))</span></span>
|
||||
<span id="cb9-115"><a href="#cb9-115" aria-hidden="true" tabindex="-1"></a><span class="st"> aufbau = time.perf_counter() - t0</span></span>
|
||||
<span id="cb9-116"><a href="#cb9-116" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter(); s.Solve(); loesen = time.perf_counter() - t0</span></span>
|
||||
<span id="cb9-117"><a href="#cb9-117" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (s.Objective().Value(), aufbau, loesen, speicher_mb())</span></span>
|
||||
<span id="cb9-118"><a href="#cb9-118" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb9-59"><a href="#cb9-59" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> speicher_mb() <span class="op">-></span> <span class="bu">float</span>:</span>
|
||||
<span id="cb9-60"><a href="#cb9-60" aria-hidden="true" tabindex="-1"></a> <span class="co"># ru_maxrss ist unter Linux in Kilobyte. Gemessen wird der Kindprozess -</span></span>
|
||||
<span id="cb9-61"><a href="#cb9-61" aria-hidden="true" tabindex="-1"></a> <span class="co"># deshalb muss jede Messung einen eigenen bekommen.</span></span>
|
||||
<span id="cb9-62"><a href="#cb9-62" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss <span class="op">/</span> <span class="dv">1024</span></span>
|
||||
<span id="cb9-63"><a href="#cb9-63" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-64"><a href="#cb9-64" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-65"><a href="#cb9-65" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe_scipy(m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb9-66"><a href="#cb9-66" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> scipy.optimize <span class="im">import</span> linprog</span>
|
||||
<span id="cb9-67"><a href="#cb9-67" aria-hidden="true" tabindex="-1"></a> kosten, angebot, bedarf <span class="op">=</span> instanz(m, n)</span>
|
||||
<span id="cb9-68"><a href="#cb9-68" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb9-69"><a href="#cb9-69" aria-hidden="true" tabindex="-1"></a> c <span class="op">=</span> kosten.reshape(<span class="op">-</span><span class="dv">1</span>)</span>
|
||||
<span id="cb9-70"><a href="#cb9-70" aria-hidden="true" tabindex="-1"></a> A_ub <span class="op">=</span> np.zeros((m, m <span class="op">*</span> n))<span class="op">;</span> A_eq <span class="op">=</span> np.zeros((n, m <span class="op">*</span> n))</span>
|
||||
<span id="cb9-71"><a href="#cb9-71" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m):</span>
|
||||
<span id="cb9-72"><a href="#cb9-72" aria-hidden="true" tabindex="-1"></a> A_ub[i, i <span class="op">*</span> n:(i <span class="op">+</span> <span class="dv">1</span>) <span class="op">*</span> n] <span class="op">=</span> <span class="fl">1.0</span></span>
|
||||
<span id="cb9-73"><a href="#cb9-73" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(n):</span>
|
||||
<span id="cb9-74"><a href="#cb9-74" aria-hidden="true" tabindex="-1"></a> A_eq[j, j::n] <span class="op">=</span> <span class="fl">1.0</span></span>
|
||||
<span id="cb9-75"><a href="#cb9-75" aria-hidden="true" tabindex="-1"></a> aufbau <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb9-76"><a href="#cb9-76" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb9-77"><a href="#cb9-77" aria-hidden="true" tabindex="-1"></a> r <span class="op">=</span> linprog(c<span class="op">=</span>c, A_ub<span class="op">=</span>A_ub, b_ub<span class="op">=</span>angebot, A_eq<span class="op">=</span>A_eq, b_eq<span class="op">=</span>bedarf,</span>
|
||||
<span id="cb9-78"><a href="#cb9-78" aria-hidden="true" tabindex="-1"></a> bounds<span class="op">=</span>(<span class="dv">0</span>, <span class="va">None</span>), method<span class="op">=</span><span class="st">"highs"</span>)</span>
|
||||
<span id="cb9-79"><a href="#cb9-79" aria-hidden="true" tabindex="-1"></a> loesen <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb9-80"><a href="#cb9-80" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">float</span>(r.fun), aufbau, loesen, speicher_mb()</span>
|
||||
<span id="cb9-81"><a href="#cb9-81" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-82"><a href="#cb9-82" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-83"><a href="#cb9-83" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe_highspy(m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb9-84"><a href="#cb9-84" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> highspy</span>
|
||||
<span id="cb9-85"><a href="#cb9-85" aria-hidden="true" tabindex="-1"></a> kosten, angebot, bedarf <span class="op">=</span> instanz(m, n)</span>
|
||||
<span id="cb9-86"><a href="#cb9-86" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb9-87"><a href="#cb9-87" aria-hidden="true" tabindex="-1"></a> h <span class="op">=</span> highspy.Highs()<span class="op">;</span> h.setOptionValue(<span class="st">"output_flag"</span>, <span class="va">False</span>)</span>
|
||||
<span id="cb9-88"><a href="#cb9-88" aria-hidden="true" tabindex="-1"></a> h.addVars(m <span class="op">*</span> n, np.zeros(m <span class="op">*</span> n), np.full(m <span class="op">*</span> n, highspy.kHighsInf))</span>
|
||||
<span id="cb9-89"><a href="#cb9-89" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(m <span class="op">*</span> n):</span>
|
||||
<span id="cb9-90"><a href="#cb9-90" aria-hidden="true" tabindex="-1"></a> h.changeColCost(k, <span class="bu">float</span>(kosten.reshape(<span class="op">-</span><span class="dv">1</span>)[k]))</span>
|
||||
<span id="cb9-91"><a href="#cb9-91" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m):</span>
|
||||
<span id="cb9-92"><a href="#cb9-92" aria-hidden="true" tabindex="-1"></a> idx <span class="op">=</span> np.arange(i <span class="op">*</span> n, (i <span class="op">+</span> <span class="dv">1</span>) <span class="op">*</span> n, dtype<span class="op">=</span>np.int32)</span>
|
||||
<span id="cb9-93"><a href="#cb9-93" aria-hidden="true" tabindex="-1"></a> h.addRow(<span class="op">-</span>highspy.kHighsInf, <span class="bu">float</span>(angebot[i]), n, idx, np.ones(n))</span>
|
||||
<span id="cb9-94"><a href="#cb9-94" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(n):</span>
|
||||
<span id="cb9-95"><a href="#cb9-95" aria-hidden="true" tabindex="-1"></a> idx <span class="op">=</span> np.arange(j, m <span class="op">*</span> n, n, dtype<span class="op">=</span>np.int32)</span>
|
||||
<span id="cb9-96"><a href="#cb9-96" aria-hidden="true" tabindex="-1"></a> h.addRow(<span class="bu">float</span>(bedarf[j]), <span class="bu">float</span>(bedarf[j]), m, idx, np.ones(m))</span>
|
||||
<span id="cb9-97"><a href="#cb9-97" aria-hidden="true" tabindex="-1"></a> aufbau <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb9-98"><a href="#cb9-98" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()<span class="op">;</span> h.run()<span class="op">;</span> loesen <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb9-99"><a href="#cb9-99" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> h.getInfo().objective_function_value, aufbau, loesen, speicher_mb()</span>
|
||||
<span id="cb9-100"><a href="#cb9-100" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-101"><a href="#cb9-101" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-102"><a href="#cb9-102" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe_ortools(m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb9-103"><a href="#cb9-103" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> ortools.linear_solver <span class="im">import</span> pywraplp</span>
|
||||
<span id="cb9-104"><a href="#cb9-104" aria-hidden="true" tabindex="-1"></a> kosten, angebot, bedarf <span class="op">=</span> instanz(m, n)</span>
|
||||
<span id="cb9-105"><a href="#cb9-105" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb9-106"><a href="#cb9-106" aria-hidden="true" tabindex="-1"></a> s <span class="op">=</span> pywraplp.Solver.CreateSolver(<span class="st">"GLOP"</span>)</span>
|
||||
<span id="cb9-107"><a href="#cb9-107" 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>i<span class="sc">}</span><span class="ss">_</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>(n)]</span>
|
||||
<span id="cb9-108"><a href="#cb9-108" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m)]</span>
|
||||
<span id="cb9-109"><a href="#cb9-109" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m):</span>
|
||||
<span id="cb9-110"><a href="#cb9-110" aria-hidden="true" tabindex="-1"></a> s.Add(<span class="bu">sum</span>(x[i]) <span class="op"><=</span> <span class="bu">float</span>(angebot[i]))</span>
|
||||
<span id="cb9-111"><a href="#cb9-111" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(n):</span>
|
||||
<span id="cb9-112"><a href="#cb9-112" aria-hidden="true" tabindex="-1"></a> s.Add(<span class="bu">sum</span>(x[i][j] <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m)) <span class="op">==</span> <span class="bu">float</span>(bedarf[j]))</span>
|
||||
<span id="cb9-113"><a href="#cb9-113" aria-hidden="true" tabindex="-1"></a> s.Minimize(<span class="bu">sum</span>(<span class="bu">float</span>(kosten[i, j]) <span class="op">*</span> x[i][j]</span>
|
||||
<span id="cb9-114"><a href="#cb9-114" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(m) <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(n)))</span>
|
||||
<span id="cb9-115"><a href="#cb9-115" aria-hidden="true" tabindex="-1"></a> aufbau <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb9-116"><a href="#cb9-116" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()<span class="op">;</span> s.Solve()<span class="op">;</span> loesen <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb9-117"><a href="#cb9-117" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> s.Objective().Value(), aufbau, loesen, speicher_mb()</span>
|
||||
<span id="cb9-118"><a href="#cb9-118" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-119"><a href="#cb9-119" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-120"><a href="#cb9-120" aria-hidden="true" tabindex="-1"></a> <span class="st">"cvxpy"</span>: <span class="st">"""</span></span>
|
||||
<span id="cb9-121"><a href="#cb9-121" aria-hidden="true" tabindex="-1"></a><span class="st"> import cvxpy as cp</span></span>
|
||||
<span id="cb9-122"><a href="#cb9-122" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter()</span></span>
|
||||
<span id="cb9-123"><a href="#cb9-123" aria-hidden="true" tabindex="-1"></a><span class="st"> x = cp.Variable((M, N), nonneg=True)</span></span>
|
||||
<span id="cb9-124"><a href="#cb9-124" aria-hidden="true" tabindex="-1"></a><span class="st"> problem = cp.Problem(cp.Minimize(cp.sum(cp.multiply(kosten, x))),</span></span>
|
||||
<span id="cb9-125"><a href="#cb9-125" aria-hidden="true" tabindex="-1"></a><span class="st"> [cp.sum(x, axis=1) <= angebot,</span></span>
|
||||
<span id="cb9-126"><a href="#cb9-126" aria-hidden="true" tabindex="-1"></a><span class="st"> cp.sum(x, axis=0) == bedarf])</span></span>
|
||||
<span id="cb9-127"><a href="#cb9-127" aria-hidden="true" tabindex="-1"></a><span class="st"> aufbau = time.perf_counter() - t0</span></span>
|
||||
<span id="cb9-128"><a href="#cb9-128" aria-hidden="true" tabindex="-1"></a><span class="st"> t0 = time.perf_counter(); problem.solve(); loesen = time.perf_counter() - t0</span></span>
|
||||
<span id="cb9-129"><a href="#cb9-129" aria-hidden="true" tabindex="-1"></a><span class="st"> ausgabe = (float(problem.value), aufbau, loesen, speicher_mb())</span></span>
|
||||
<span id="cb9-130"><a href="#cb9-130" aria-hidden="true" tabindex="-1"></a><span class="st"> """</span>,</span>
|
||||
<span id="cb9-131"><a href="#cb9-131" aria-hidden="true" tabindex="-1"></a>}</span>
|
||||
<span id="cb9-120"><a href="#cb9-120" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe_cvxpy(m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb9-121"><a href="#cb9-121" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> cvxpy <span class="im">as</span> cp</span>
|
||||
<span id="cb9-122"><a href="#cb9-122" aria-hidden="true" tabindex="-1"></a> kosten, angebot, bedarf <span class="op">=</span> instanz(m, n)</span>
|
||||
<span id="cb9-123"><a href="#cb9-123" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
|
||||
<span id="cb9-124"><a href="#cb9-124" aria-hidden="true" tabindex="-1"></a> x <span class="op">=</span> cp.Variable((m, n), nonneg<span class="op">=</span><span class="va">True</span>)</span>
|
||||
<span id="cb9-125"><a href="#cb9-125" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> cp.Problem(cp.Minimize(cp.<span class="bu">sum</span>(cp.multiply(kosten, x))),</span>
|
||||
<span id="cb9-126"><a href="#cb9-126" aria-hidden="true" tabindex="-1"></a> [cp.<span class="bu">sum</span>(x, axis<span class="op">=</span><span class="dv">1</span>) <span class="op"><=</span> angebot,</span>
|
||||
<span id="cb9-127"><a href="#cb9-127" aria-hidden="true" tabindex="-1"></a> cp.<span class="bu">sum</span>(x, axis<span class="op">=</span><span class="dv">0</span>) <span class="op">==</span> bedarf])</span>
|
||||
<span id="cb9-128"><a href="#cb9-128" aria-hidden="true" tabindex="-1"></a> aufbau <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb9-129"><a href="#cb9-129" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()<span class="op">;</span> problem.solve()<span class="op">;</span> loesen <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
|
||||
<span id="cb9-130"><a href="#cb9-130" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">float</span>(problem.value), aufbau, loesen, speicher_mb()</span>
|
||||
<span id="cb9-131"><a href="#cb9-131" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-132"><a href="#cb9-132" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-133"><a href="#cb9-133" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-134"><a href="#cb9-134" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe(name: <span class="bu">str</span>, quelltext: <span class="bu">str</span>, m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb9-135"><a href="#cb9-135" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Fuehrt einen Ansatz in einem eigenen Prozess aus."""</span></span>
|
||||
<span id="cb9-136"><a href="#cb9-136" aria-hidden="true" tabindex="-1"></a> programm <span class="op">=</span> (VORSPANN.<span class="bu">format</span>(m<span class="op">=</span>m, n<span class="op">=</span>n) <span class="op">+</span> textwrap.dedent(quelltext)</span>
|
||||
<span id="cb9-137"><a href="#cb9-137" aria-hidden="true" tabindex="-1"></a> <span class="op">+</span> <span class="st">"</span><span class="ch">\n</span><span class="st">print(json.dumps(ausgabe))</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
||||
<span id="cb9-138"><a href="#cb9-138" aria-hidden="true" tabindex="-1"></a> ergebnis <span class="op">=</span> subprocess.run([sys.executable, <span class="st">"-c"</span>, programm],</span>
|
||||
<span id="cb9-139"><a href="#cb9-139" 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">600</span>)</span>
|
||||
<span id="cb9-140"><a href="#cb9-140" 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="cb9-141"><a href="#cb9-141" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span>, ergebnis.stderr.strip().splitlines()[<span class="op">-</span><span class="dv">1</span>][:<span class="dv">60</span>]</span>
|
||||
<span id="cb9-142"><a href="#cb9-142" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> json.loads(ergebnis.stdout.strip().splitlines()[<span class="op">-</span><span class="dv">1</span>]), <span class="va">None</span></span>
|
||||
<span id="cb9-143"><a href="#cb9-143" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-144"><a href="#cb9-144" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-145"><a href="#cb9-145" 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="cb9-146"><a href="#cb9-146" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb9-147"><a href="#cb9-147" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" SKALIERUNGSVERGLEICH: TRANSPORTPROBLEM, VIER BIBLIOTHEKEN"</span>)</span>
|
||||
<span id="cb9-148"><a href="#cb9-148" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb9-149"><a href="#cb9-149" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Jede Zeile ein eigener Prozess. Zeiten und Speicher sind "</span></span>
|
||||
<span id="cb9-150"><a href="#cb9-150" aria-hidden="true" tabindex="-1"></a> <span class="st">"hardwareabhaengig,"</span>)</span>
|
||||
<span id="cb9-151"><a href="#cb9-151" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"die Zielwerte und ihr Verhaeltnis zueinander nicht.</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
||||
<span id="cb9-152"><a href="#cb9-152" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-153"><a href="#cb9-153" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> m, n <span class="kw">in</span> GROESSEN:</span>
|
||||
<span id="cb9-154"><a href="#cb9-154" aria-hidden="true" tabindex="-1"></a> kopf <span class="op">=</span> <span class="ss">f"--- </span><span class="sc">{</span>m<span class="sc">}</span><span class="ss"> Lager x </span><span class="sc">{</span>n<span class="sc">}</span><span class="ss"> Kunden = </span><span class="sc">{</span>m <span class="op">*</span> n<span class="sc">:,}</span><span class="ss"> Variablen "</span></span>
|
||||
<span id="cb9-155"><a href="#cb9-155" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(kopf <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="bu">max</span>(<span class="dv">3</span>, <span class="dv">92</span> <span class="op">-</span> <span class="bu">len</span>(kopf)))</span>
|
||||
<span id="cb9-156"><a href="#cb9-156" 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">:<16}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Zielwert'</span><span class="sc">:>14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Aufbau'</span><span class="sc">:>9}</span><span class="ss"> "</span></span>
|
||||
<span id="cb9-157"><a href="#cb9-157" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'Loesen'</span><span class="sc">:>9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Anteil'</span><span class="sc">:>8}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Speicher'</span><span class="sc">:>10}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb9-158"><a href="#cb9-158" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" "</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">72</span>)</span>
|
||||
<span id="cb9-159"><a href="#cb9-159" aria-hidden="true" tabindex="-1"></a> zielwerte <span class="op">=</span> {}</span>
|
||||
<span id="cb9-160"><a href="#cb9-160" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, quelltext <span class="kw">in</span> ANSAETZE.items():</span>
|
||||
<span id="cb9-161"><a href="#cb9-161" aria-hidden="true" tabindex="-1"></a> werte, fehler <span class="op">=</span> messe(name, quelltext, m, n)</span>
|
||||
<span id="cb9-162"><a href="#cb9-162" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> werte <span class="kw">is</span> <span class="va">None</span>:</span>
|
||||
<span id="cb9-163"><a href="#cb9-163" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>name<span class="sc">:<16}</span><span class="ss"> nicht verfuegbar: </span><span class="sc">{</span>fehler<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb9-164"><a href="#cb9-164" aria-hidden="true" tabindex="-1"></a> <span class="cf">continue</span></span>
|
||||
<span id="cb9-165"><a href="#cb9-165" aria-hidden="true" tabindex="-1"></a> ziel, aufbau, loesen, speicher <span class="op">=</span> werte</span>
|
||||
<span id="cb9-166"><a href="#cb9-166" aria-hidden="true" tabindex="-1"></a> zielwerte[name] <span class="op">=</span> ziel</span>
|
||||
<span id="cb9-167"><a href="#cb9-167" aria-hidden="true" tabindex="-1"></a> anteil <span class="op">=</span> aufbau <span class="op">/</span> (aufbau <span class="op">+</span> loesen) <span class="op">*</span> <span class="dv">100</span></span>
|
||||
<span id="cb9-168"><a href="#cb9-168" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>name<span class="sc">:<16}</span><span class="ss"> </span><span class="sc">{</span>ziel<span class="sc">:>14,.2f}</span><span class="ss"> </span><span class="sc">{</span>aufbau<span class="sc">:>8.3f}</span><span class="ss">s "</span></span>
|
||||
<span id="cb9-169"><a href="#cb9-169" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>loesen<span class="sc">:>8.3f}</span><span class="ss">s </span><span class="sc">{</span>anteil<span class="sc">:>7.0f}</span><span class="ss">% </span><span class="sc">{</span>speicher<span class="sc">:>9.0f}</span><span class="ss"> MB"</span>)</span>
|
||||
<span id="cb9-170"><a href="#cb9-170" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-171"><a href="#cb9-171" aria-hidden="true" tabindex="-1"></a> <span class="co"># Die wichtigste Zeile: Rechnen alle dasselbe aus?</span></span>
|
||||
<span id="cb9-172"><a href="#cb9-172" aria-hidden="true" tabindex="-1"></a> spanne <span class="op">=</span> <span class="bu">max</span>(zielwerte.values()) <span class="op">-</span> <span class="bu">min</span>(zielwerte.values())</span>
|
||||
<span id="cb9-173"><a href="#cb9-173" aria-hidden="true" tabindex="-1"></a> bezug <span class="op">=</span> <span class="bu">max</span>(<span class="bu">abs</span>(v) <span class="cf">for</span> v <span class="kw">in</span> zielwerte.values())</span>
|
||||
<span id="cb9-174"><a href="#cb9-174" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">''</span><span class="sc">:16}</span><span class="ss"> Spannweite der Zielwerte: </span><span class="sc">{</span>spanne<span class="sc">:.2e}</span><span class="ss"> "</span></span>
|
||||
<span id="cb9-175"><a href="#cb9-175" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"(relativ </span><span class="sc">{</span>spanne <span class="op">/</span> bezug<span class="sc">:.1e}</span><span class="ss">)"</span>)</span>
|
||||
<span id="cb9-176"><a href="#cb9-176" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> spanne <span class="op">/</span> bezug <span class="op">></span> <span class="fl">1e-6</span>:</span>
|
||||
<span id="cb9-177"><a href="#cb9-177" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ACHTUNG: Die Bibliotheken widersprechen sich - "</span></span>
|
||||
<span id="cb9-178"><a href="#cb9-178" aria-hidden="true" tabindex="-1"></a> <span class="st">"der Zeitvergleich ist wertlos."</span>)</span>
|
||||
<span id="cb9-179"><a href="#cb9-179" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb9-180"><a href="#cb9-180" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-181"><a href="#cb9-181" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb9-182"><a href="#cb9-182" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" WAS MAN AUS SO EINER TABELLE ABLESEN DARF - UND WAS NICHT"</span>)</span>
|
||||
<span id="cb9-183"><a href="#cb9-183" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb9-184"><a href="#cb9-184" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"DARF man ablesen:"</span>)</span>
|
||||
<span id="cb9-185"><a href="#cb9-185" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Die Spalte 'Anteil' - wie viel der Zeit in den AUFBAU geht statt"</span>)</span>
|
||||
<span id="cb9-186"><a href="#cb9-186" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ins Loesen. Wenn dort 80 </span><span class="sc">% s</span><span class="st">tehen, ist ein schnellerer Solver die"</span>)</span>
|
||||
<span id="cb9-187"><a href="#cb9-187" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" falsche Antwort; dann gehoert das Modell vektorisiert aufgebaut"</span>)</span>
|
||||
<span id="cb9-188"><a href="#cb9-188" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" (Kapitel Oekosystem)."</span>)</span>
|
||||
<span id="cb9-189"><a href="#cb9-189" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Die Groessenordnung des Speicherbedarfs. Sie entscheidet, was auf"</span>)</span>
|
||||
<span id="cb9-190"><a href="#cb9-190" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" einer bestimmten Maschine ueberhaupt laeuft."</span>)</span>
|
||||
<span id="cb9-191"><a href="#cb9-191" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Wie sich beides mit der Groesse ENTWICKELT. Der Trend ist"</span>)</span>
|
||||
<span id="cb9-192"><a href="#cb9-192" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" uebertragbarer als der Absolutwert."</span>)</span>
|
||||
<span id="cb9-193"><a href="#cb9-193" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb9-194"><a href="#cb9-194" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"NICHT ablesen darf man:"</span>)</span>
|
||||
<span id="cb9-195"><a href="#cb9-195" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * 'Bibliothek X ist schneller als Y.' Gemessen wurde EIN"</span>)</span>
|
||||
<span id="cb9-196"><a href="#cb9-196" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Problemtyp in EINER Formulierung. Ein MILP, ein QP oder eine"</span>)</span>
|
||||
<span id="cb9-197"><a href="#cb9-197" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" andere Modellierung desselben Problems koennen die Reihenfolge"</span>)</span>
|
||||
<span id="cb9-198"><a href="#cb9-198" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" umdrehen."</span>)</span>
|
||||
<span id="cb9-199"><a href="#cb9-199" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Etwas ueber Ihre Maschine. Diese Zahlen stammen von einer"</span>)</span>
|
||||
<span id="cb9-200"><a href="#cb9-200" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" anderen. Der Sinn des Programms ist, dass Sie es auf Ihrer"</span>)</span>
|
||||
<span id="cb9-201"><a href="#cb9-201" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" laufen lassen."</span>)</span>
|
||||
<span id="cb9-202"><a href="#cb9-202" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span></code></pre></div>
|
||||
<span id="cb9-133"><a href="#cb9-133" aria-hidden="true" tabindex="-1"></a>ANSAETZE <span class="op">=</span> {<span class="st">"scipy.linprog"</span>: messe_scipy, <span class="st">"highspy"</span>: messe_highspy,</span>
|
||||
<span id="cb9-134"><a href="#cb9-134" aria-hidden="true" tabindex="-1"></a> <span class="st">"ortools/GLOP"</span>: messe_ortools, <span class="st">"cvxpy"</span>: messe_cvxpy}</span>
|
||||
<span id="cb9-135"><a href="#cb9-135" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-136"><a href="#cb9-136" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-137"><a href="#cb9-137" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> messe(funktion, m: <span class="bu">int</span>, n: <span class="bu">int</span>):</span>
|
||||
<span id="cb9-138"><a href="#cb9-138" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Fuehrt eine Messfunktion in einem FRISCHEN Prozess aus.</span></span>
|
||||
<span id="cb9-139"><a href="#cb9-139" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-140"><a href="#cb9-140" aria-hidden="true" tabindex="-1"></a><span class="co"> 'spawn' und max_tasks_per_child=1 zusammen garantieren, was Regel 1</span></span>
|
||||
<span id="cb9-141"><a href="#cb9-141" aria-hidden="true" tabindex="-1"></a><span class="co"> verlangt: Jede Messung sieht einen leeren Interpreter. Ohne das</span></span>
|
||||
<span id="cb9-142"><a href="#cb9-142" aria-hidden="true" tabindex="-1"></a><span class="co"> zweite wuerde der Pool seinen Arbeiter wiederverwenden - dann waere</span></span>
|
||||
<span id="cb9-143"><a href="#cb9-143" aria-hidden="true" tabindex="-1"></a><span class="co"> der Speicherwert der zweiten Bibliothek um die erste zu hoch, und</span></span>
|
||||
<span id="cb9-144"><a href="#cb9-144" aria-hidden="true" tabindex="-1"></a><span class="co"> ortools und highspy saessen im selben Prozess.</span></span>
|
||||
<span id="cb9-145"><a href="#cb9-145" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
|
||||
<span id="cb9-146"><a href="#cb9-146" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> ProcessPoolExecutor(</span>
|
||||
<span id="cb9-147"><a href="#cb9-147" aria-hidden="true" tabindex="-1"></a> max_workers<span class="op">=</span><span class="dv">1</span>,</span>
|
||||
<span id="cb9-148"><a href="#cb9-148" aria-hidden="true" tabindex="-1"></a> mp_context<span class="op">=</span>multiprocessing.get_context(<span class="st">"spawn"</span>),</span>
|
||||
<span id="cb9-149"><a href="#cb9-149" 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="cb9-150"><a href="#cb9-150" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span>:</span>
|
||||
<span id="cb9-151"><a href="#cb9-151" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> pool.submit(funktion, m, n).result(timeout<span class="op">=</span><span class="dv">600</span>), <span class="va">None</span></span>
|
||||
<span id="cb9-152"><a href="#cb9-152" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> <span class="pp">Exception</span> <span class="im">as</span> fehler:</span>
|
||||
<span id="cb9-153"><a href="#cb9-153" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span>, <span class="bu">str</span>(fehler).strip().splitlines()[<span class="op">-</span><span class="dv">1</span>][:<span class="dv">60</span>]</span>
|
||||
<span id="cb9-154"><a href="#cb9-154" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-155"><a href="#cb9-155" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-156"><a href="#cb9-156" 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="cb9-157"><a href="#cb9-157" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb9-158"><a href="#cb9-158" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" SKALIERUNGSVERGLEICH: TRANSPORTPROBLEM, VIER BIBLIOTHEKEN"</span>)</span>
|
||||
<span id="cb9-159"><a href="#cb9-159" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb9-160"><a href="#cb9-160" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Jede Zeile ein eigener Prozess. Zeiten und Speicher sind "</span></span>
|
||||
<span id="cb9-161"><a href="#cb9-161" aria-hidden="true" tabindex="-1"></a> <span class="st">"hardwareabhaengig,"</span>)</span>
|
||||
<span id="cb9-162"><a href="#cb9-162" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"die Zielwerte und ihr Verhaeltnis zueinander nicht.</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
||||
<span id="cb9-163"><a href="#cb9-163" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-164"><a href="#cb9-164" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> m, n <span class="kw">in</span> GROESSEN:</span>
|
||||
<span id="cb9-165"><a href="#cb9-165" aria-hidden="true" tabindex="-1"></a> kopf <span class="op">=</span> <span class="ss">f"--- </span><span class="sc">{</span>m<span class="sc">}</span><span class="ss"> Lager x </span><span class="sc">{</span>n<span class="sc">}</span><span class="ss"> Kunden = </span><span class="sc">{</span>m <span class="op">*</span> n<span class="sc">:,}</span><span class="ss"> Variablen "</span></span>
|
||||
<span id="cb9-166"><a href="#cb9-166" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(kopf <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="bu">max</span>(<span class="dv">3</span>, <span class="dv">92</span> <span class="op">-</span> <span class="bu">len</span>(kopf)))</span>
|
||||
<span id="cb9-167"><a href="#cb9-167" 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">:<16}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Zielwert'</span><span class="sc">:>14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Aufbau'</span><span class="sc">:>9}</span><span class="ss"> "</span></span>
|
||||
<span id="cb9-168"><a href="#cb9-168" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'Loesen'</span><span class="sc">:>9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Anteil'</span><span class="sc">:>8}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Speicher'</span><span class="sc">:>10}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb9-169"><a href="#cb9-169" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" "</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">72</span>)</span>
|
||||
<span id="cb9-170"><a href="#cb9-170" aria-hidden="true" tabindex="-1"></a> zielwerte <span class="op">=</span> {}</span>
|
||||
<span id="cb9-171"><a href="#cb9-171" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, funktion <span class="kw">in</span> ANSAETZE.items():</span>
|
||||
<span id="cb9-172"><a href="#cb9-172" aria-hidden="true" tabindex="-1"></a> werte, fehler <span class="op">=</span> messe(funktion, m, n)</span>
|
||||
<span id="cb9-173"><a href="#cb9-173" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> werte <span class="kw">is</span> <span class="va">None</span>:</span>
|
||||
<span id="cb9-174"><a href="#cb9-174" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>name<span class="sc">:<16}</span><span class="ss"> nicht verfuegbar: </span><span class="sc">{</span>fehler<span class="sc">}</span><span class="ss">"</span>)</span>
|
||||
<span id="cb9-175"><a href="#cb9-175" aria-hidden="true" tabindex="-1"></a> <span class="cf">continue</span></span>
|
||||
<span id="cb9-176"><a href="#cb9-176" aria-hidden="true" tabindex="-1"></a> ziel, aufbau, loesen, speicher <span class="op">=</span> werte</span>
|
||||
<span id="cb9-177"><a href="#cb9-177" aria-hidden="true" tabindex="-1"></a> zielwerte[name] <span class="op">=</span> ziel</span>
|
||||
<span id="cb9-178"><a href="#cb9-178" aria-hidden="true" tabindex="-1"></a> anteil <span class="op">=</span> aufbau <span class="op">/</span> (aufbau <span class="op">+</span> loesen) <span class="op">*</span> <span class="dv">100</span></span>
|
||||
<span id="cb9-179"><a href="#cb9-179" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>name<span class="sc">:<16}</span><span class="ss"> </span><span class="sc">{</span>ziel<span class="sc">:>14,.2f}</span><span class="ss"> </span><span class="sc">{</span>aufbau<span class="sc">:>8.3f}</span><span class="ss">s "</span></span>
|
||||
<span id="cb9-180"><a href="#cb9-180" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>loesen<span class="sc">:>8.3f}</span><span class="ss">s </span><span class="sc">{</span>anteil<span class="sc">:>7.0f}</span><span class="ss">% </span><span class="sc">{</span>speicher<span class="sc">:>9.0f}</span><span class="ss"> MB"</span>)</span>
|
||||
<span id="cb9-181"><a href="#cb9-181" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-182"><a href="#cb9-182" aria-hidden="true" tabindex="-1"></a> <span class="co"># Die wichtigste Zeile: Rechnen alle dasselbe aus?</span></span>
|
||||
<span id="cb9-183"><a href="#cb9-183" aria-hidden="true" tabindex="-1"></a> spanne <span class="op">=</span> <span class="bu">max</span>(zielwerte.values()) <span class="op">-</span> <span class="bu">min</span>(zielwerte.values())</span>
|
||||
<span id="cb9-184"><a href="#cb9-184" aria-hidden="true" tabindex="-1"></a> bezug <span class="op">=</span> <span class="bu">max</span>(<span class="bu">abs</span>(v) <span class="cf">for</span> v <span class="kw">in</span> zielwerte.values())</span>
|
||||
<span id="cb9-185"><a href="#cb9-185" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">''</span><span class="sc">:16}</span><span class="ss"> Spannweite der Zielwerte: </span><span class="sc">{</span>spanne<span class="sc">:.2e}</span><span class="ss"> "</span></span>
|
||||
<span id="cb9-186"><a href="#cb9-186" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"(relativ </span><span class="sc">{</span>spanne <span class="op">/</span> bezug<span class="sc">:.1e}</span><span class="ss">)"</span>)</span>
|
||||
<span id="cb9-187"><a href="#cb9-187" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> spanne <span class="op">/</span> bezug <span class="op">></span> <span class="fl">1e-6</span>:</span>
|
||||
<span id="cb9-188"><a href="#cb9-188" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ACHTUNG: Die Bibliotheken widersprechen sich - "</span></span>
|
||||
<span id="cb9-189"><a href="#cb9-189" aria-hidden="true" tabindex="-1"></a> <span class="st">"der Zeitvergleich ist wertlos."</span>)</span>
|
||||
<span id="cb9-190"><a href="#cb9-190" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb9-191"><a href="#cb9-191" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb9-192"><a href="#cb9-192" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb9-193"><a href="#cb9-193" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" WAS MAN AUS SO EINER TABELLE ABLESEN DARF - UND WAS NICHT"</span>)</span>
|
||||
<span id="cb9-194"><a href="#cb9-194" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span>
|
||||
<span id="cb9-195"><a href="#cb9-195" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"DARF man ablesen:"</span>)</span>
|
||||
<span id="cb9-196"><a href="#cb9-196" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Die Spalte 'Anteil' - wie viel der Zeit in den AUFBAU geht statt"</span>)</span>
|
||||
<span id="cb9-197"><a href="#cb9-197" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ins Loesen. Wenn dort 80 </span><span class="sc">% s</span><span class="st">tehen, ist ein schnellerer Solver die"</span>)</span>
|
||||
<span id="cb9-198"><a href="#cb9-198" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" falsche Antwort; dann gehoert das Modell vektorisiert aufgebaut"</span>)</span>
|
||||
<span id="cb9-199"><a href="#cb9-199" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" (Kapitel Oekosystem)."</span>)</span>
|
||||
<span id="cb9-200"><a href="#cb9-200" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Die Groessenordnung des Speicherbedarfs. Sie entscheidet, was auf"</span>)</span>
|
||||
<span id="cb9-201"><a href="#cb9-201" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" einer bestimmten Maschine ueberhaupt laeuft."</span>)</span>
|
||||
<span id="cb9-202"><a href="#cb9-202" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Wie sich beides mit der Groesse ENTWICKELT. Der Trend ist"</span>)</span>
|
||||
<span id="cb9-203"><a href="#cb9-203" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" uebertragbarer als der Absolutwert."</span>)</span>
|
||||
<span id="cb9-204"><a href="#cb9-204" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
|
||||
<span id="cb9-205"><a href="#cb9-205" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"NICHT ablesen darf man:"</span>)</span>
|
||||
<span id="cb9-206"><a href="#cb9-206" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * 'Bibliothek X ist schneller als Y.' Gemessen wurde EIN"</span>)</span>
|
||||
<span id="cb9-207"><a href="#cb9-207" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Problemtyp in EINER Formulierung. Ein MILP, ein QP oder eine"</span>)</span>
|
||||
<span id="cb9-208"><a href="#cb9-208" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" andere Modellierung desselben Problems koennen die Reihenfolge"</span>)</span>
|
||||
<span id="cb9-209"><a href="#cb9-209" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" umdrehen."</span>)</span>
|
||||
<span id="cb9-210"><a href="#cb9-210" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" * Etwas ueber Ihre Maschine. Diese Zahlen stammen von einer"</span>)</span>
|
||||
<span id="cb9-211"><a href="#cb9-211" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" anderen. Der Sinn des Programms ist, dass Sie es auf Ihrer"</span>)</span>
|
||||
<span id="cb9-212"><a href="#cb9-212" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" laufen lassen."</span>)</span>
|
||||
<span id="cb9-213"><a href="#cb9-213" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">92</span>)</span></code></pre></div>
|
||||
<p><strong>Erwartete Ausgabe</strong> (Zeiten und Speicher hardwareabhängig, die Zielwerte nicht):</p>
|
||||
<pre><code>============================================================================================
|
||||
SKALIERUNGSVERGLEICH: TRANSPORTPROBLEM, VIER BIBLIOTHEKEN
|
||||
|
|
@ -1042,28 +1053,28 @@ die Zielwerte und ihr Verhaeltnis zueinander nicht.
|
|||
--- 10 Lager x 10 Kunden = 100 Variablen ---------------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 13,509.48 0.000s 0.004s 1% 78 MB
|
||||
highspy 13,509.48 0.001s 0.002s 34% 41 MB
|
||||
ortools/GLOP 13,509.48 0.002s 0.001s 80% 54 MB
|
||||
scipy.linprog 13,509.48 0.000s 0.004s 1% 79 MB
|
||||
highspy 13,509.48 0.001s 0.002s 36% 44 MB
|
||||
ortools/GLOP 13,509.48 0.003s 0.001s 80% 56 MB
|
||||
cvxpy 13,509.48 0.001s 0.009s 9% 229 MB
|
||||
Spannweite der Zielwerte: 1.33e-06 (relativ 9.8e-11)
|
||||
|
||||
--- 32 Lager x 32 Kunden = 1,024 Variablen -------------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 35,744.25 0.000s 0.009s 4% 80 MB
|
||||
highspy 35,744.25 0.004s 0.005s 47% 42 MB
|
||||
ortools/GLOP 35,744.25 0.015s 0.002s 85% 55 MB
|
||||
cvxpy 35,744.25 0.001s 0.016s 5% 230 MB
|
||||
scipy.linprog 35,744.25 0.000s 0.009s 3% 81 MB
|
||||
highspy 35,744.25 0.004s 0.005s 44% 45 MB
|
||||
ortools/GLOP 35,744.25 0.014s 0.002s 85% 58 MB
|
||||
cvxpy 35,744.25 0.001s 0.017s 5% 231 MB
|
||||
Spannweite der Zielwerte: 9.54e-05 (relativ 2.7e-09)
|
||||
|
||||
--- 100 Lager x 100 Kunden = 10,000 Variablen ----------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 72,220.34 0.002s 0.070s 3% 120 MB
|
||||
highspy 72,220.34 0.029s 0.034s 45% 47 MB
|
||||
ortools/GLOP 72,220.34 0.123s 0.036s 77% 66 MB
|
||||
cvxpy 72,220.34 0.001s 0.103s 1% 242 MB
|
||||
scipy.linprog 72,220.34 0.004s 0.072s 5% 122 MB
|
||||
highspy 72,220.34 0.026s 0.032s 45% 50 MB
|
||||
ortools/GLOP 72,220.34 0.141s 0.043s 77% 68 MB
|
||||
cvxpy 72,220.34 0.001s 0.108s 1% 242 MB
|
||||
Spannweite der Zielwerte: 3.65e-04 (relativ 5.0e-09)
|
||||
|
||||
============================================================================================
|
||||
|
|
|
|||
|
|
@ -3127,6 +3127,21 @@ $$
|
|||
>
|
||||
> Der Installationstest im Vorspann umgeht die Falle bereits: Er lädt `ortools` zuerst, prüft `highspy` und `cvxpy` in der Paketübersicht nur auf Anwesenheit (`importlib.util.find_spec`) und importiert CVXPY erst im Funktionstest.
|
||||
|
||||
### Wie die Isolation aussieht, wenn sie tragen soll
|
||||
|
||||
„Eigener Prozess" ist schnell gesagt. Die naheliegende Umsetzung — ein Codeschnipsel als Zeichenkette an `python -c` ü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.
|
||||
|
||||
Tragfähig ist stattdessen: **jeder Solver eine gewöhnliche Funktion mit lokalem Import**, ausgeführt von einem `ProcessPoolExecutor` mit zwei Einstellungen, die zusammen die Garantie ergeben:
|
||||
|
||||
| Einstellung | Wozu |
|
||||
| --- | --- |
|
||||
| `mp_context=multiprocessing.get_context("spawn")` | Der Kindprozess startet mit einem **frischen** Interpreter, statt den Speicher des Elternprozesses zu erben. Unter Linux ist `fork` der Standard — und damit wäre alles, was hier schon importiert ist, auch dort importiert. |
|
||||
| `max_tasks_per_child=1` | Jede Aufgabe bekommt einen **neuen** 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. |
|
||||
|
||||
> **⚠️ `max_tasks_per_child=1` ist nicht optional** Ein Pool ohne diese Angabe ist der **Normalfall** — 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.
|
||||
|
||||
Denselben Aufbau verwenden `Solverwechsel_CPSAT_HiGHS.py` ([Kapitel 22](#kap-praxisfallen)) und `Benchmark_Skalierung.py` ([Kapitel 23](#kap-testing)). Dort wandern zusätzlich **Datenobjekte** über die Prozessgrenze statt Zeichenketten — möglich, weil Domänenmodell und Lösungs-DTO keinen Solver kennen ([Abschnitt 22.6](#sec:praxisfallen-or-kern)).
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
|
@ -3138,85 +3153,104 @@ Kapitel Oekosystem: Dasselbe LP in vier Bibliotheken.
|
|||
2*x1 + 3*x2 + x3 <= 50
|
||||
x >= 0
|
||||
|
||||
Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich am Ende.
|
||||
Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich
|
||||
am Ende.
|
||||
|
||||
WICHTIG: Jeder Solver läuft in einem EIGENEN Prozess, weil sich ortools und
|
||||
WICHTIG: Jeder Solver laeuft in einem EIGENEN Prozess, weil sich ortools und
|
||||
highspy auf vielen Systemen nicht gemeinsam importieren lassen (beide bringen
|
||||
eine eigene HiGHS-Kopie mit -> Symbolkonflikt).
|
||||
|
||||
Die Isolation besorgt ein ProcessPoolExecutor. Drei Einstellungen ergeben
|
||||
zusammen die Garantie:
|
||||
|
||||
mp_context "spawn" Der Kindprozess startet mit einem FRISCHEN
|
||||
Interpreter, statt den Speicher des Elternprozesses
|
||||
zu erben. Was hier schon importiert ist, ist dort
|
||||
nicht importiert. Mit dem Standard "fork" auf Linux
|
||||
waere das nicht so.
|
||||
max_tasks_per_child=1 Jede Aufgabe bekommt einen NEUEN Prozess. Ohne das
|
||||
wuerde der Pool seinen Arbeiter wiederverwenden - und
|
||||
beim zweiten Solver waere der Konflikt zurueck.
|
||||
max_workers=1 Haelt die vier Laeufe nacheinander. Nicht aus
|
||||
Vorsicht, sondern damit die gemessenen Zeiten
|
||||
vergleichbar bleiben.
|
||||
|
||||
Jeder Solver steht in einer eigenen Funktion mit LOKALEM Import. Das ist der
|
||||
Unterschied zu einem Codestring, den man an 'python -c' uebergibt: Die
|
||||
Funktion laesst sich einzeln aufrufen, testen und vom Editor pruefen - ein
|
||||
String nicht.
|
||||
|
||||
Benoetigt: scipy, highspy, cvxpy, ortools
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import multiprocessing
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
ERWARTET = 530.0 # Ergebnis der Handrechnung zum Produktionsprogramm
|
||||
|
||||
# Jeder Eintrag ist ein eigenständiges Miniprogramm, das sein Ergebnis als
|
||||
# JSON auf stdout ausgibt. So bleibt jeder Import in seinem eigenen Prozess.
|
||||
ANSAETZE: dict[str, str] = {
|
||||
# Die Instanz - einmal notiert, von allen vier Funktionen benutzt.
|
||||
ZIEL = [10.0, 15.0, 25.0]
|
||||
MATRIX = [[1, 1, 2], [2, 3, 1]]
|
||||
KAPAZITAET = [40.0, 50.0]
|
||||
|
||||
"scipy.optimize.linprog": """
|
||||
|
||||
def loese_mit_scipy() -> tuple[float, list[float]]:
|
||||
from scipy.optimize import linprog
|
||||
res = linprog(c=[-10.0, -15.0, -25.0], # linprog MINIMIERT -> negieren
|
||||
A_ub=[[1, 1, 2], [2, 3, 1]], b_ub=[40, 50],
|
||||
ergebnis = linprog(c=[-w for w in ZIEL], # linprog MINIMIERT -> negieren
|
||||
A_ub=MATRIX, b_ub=KAPAZITAET,
|
||||
bounds=[(0, None)] * 3, method="highs")
|
||||
ausgabe = (-res.fun, list(res.x))
|
||||
""",
|
||||
return -ergebnis.fun, list(ergebnis.x)
|
||||
|
||||
"highspy (natives HiGHS)": """
|
||||
import numpy as np, highspy
|
||||
|
||||
def loese_mit_highspy() -> tuple[float, list[float]]:
|
||||
import highspy
|
||||
import numpy as np
|
||||
h = highspy.Highs()
|
||||
h.setOptionValue("output_flag", False)
|
||||
h.addVars(3, np.zeros(3), np.full(3, highspy.kHighsInf))
|
||||
h.changeObjectiveSense(highspy.ObjSense.kMaximize)
|
||||
for j, wert in enumerate([10.0, 15.0, 25.0]):
|
||||
for j, wert in enumerate(ZIEL):
|
||||
h.changeColCost(j, wert)
|
||||
# CSR-Format: starts[i] = Beginn von Zeile i in indices/values
|
||||
h.addRows(2, np.full(2, -highspy.kHighsInf), np.array([40.0, 50.0]), 6,
|
||||
h.addRows(2, np.full(2, -highspy.kHighsInf), np.array(KAPAZITAET), 6,
|
||||
np.array([0, 3], dtype=np.int32),
|
||||
np.array([0, 1, 2, 0, 1, 2], dtype=np.int32),
|
||||
np.array([1.0, 1.0, 2.0, 2.0, 3.0, 1.0]))
|
||||
np.array([float(w) for zeile in MATRIX for w in zeile]))
|
||||
h.run()
|
||||
ausgabe = (h.getInfo().objective_function_value,
|
||||
return (h.getInfo().objective_function_value,
|
||||
list(h.getSolution().col_value[:3]))
|
||||
""",
|
||||
|
||||
"cvxpy": """
|
||||
import numpy as np, cvxpy as cp
|
||||
|
||||
def loese_mit_cvxpy() -> tuple[float, list[float]]:
|
||||
import cvxpy as cp
|
||||
import numpy as np
|
||||
x = cp.Variable(3, nonneg=True)
|
||||
problem = cp.Problem(cp.Maximize(np.array([10.0, 15.0, 25.0]) @ x),
|
||||
[np.array([[1, 1, 2], [2, 3, 1]]) @ x <= np.array([40, 50])])
|
||||
problem = cp.Problem(cp.Maximize(np.array(ZIEL) @ x),
|
||||
[np.array(MATRIX) @ x <= np.array(KAPAZITAET)])
|
||||
problem.solve()
|
||||
ausgabe = (float(problem.value), [float(v) for v in x.value])
|
||||
""",
|
||||
return float(problem.value), [float(v) for v in x.value]
|
||||
|
||||
"ortools / GLOP": """
|
||||
|
||||
def loese_mit_ortools() -> tuple[float, list[float]]:
|
||||
from ortools.linear_solver import pywraplp
|
||||
s = pywraplp.Solver.CreateSolver("GLOP")
|
||||
x = [s.NumVar(0, s.infinity(), f"x{j+1}") for j in range(3)]
|
||||
A = [[1, 1, 2], [2, 3, 1]]
|
||||
for i, kap in enumerate([40, 50]):
|
||||
s.Add(sum(A[i][j] * x[j] for j in range(3)) <= kap)
|
||||
s.Maximize(10 * x[0] + 15 * x[1] + 25 * x[2])
|
||||
for i, kapazitaet in enumerate(KAPAZITAET):
|
||||
s.Add(sum(MATRIX[i][j] * x[j] for j in range(3)) <= kapazitaet)
|
||||
s.Maximize(sum(ZIEL[j] * x[j] for j in range(3)))
|
||||
s.Solve()
|
||||
ausgabe = (s.Objective().Value(), [v.solution_value() for v in x])
|
||||
""",
|
||||
return s.Objective().Value(), [v.solution_value() for v in x]
|
||||
|
||||
|
||||
ANSAETZE = {
|
||||
"scipy.optimize.linprog": loese_mit_scipy,
|
||||
"highspy (natives HiGHS)": loese_mit_highspy,
|
||||
"cvxpy": loese_mit_cvxpy,
|
||||
"ortools / GLOP": loese_mit_ortools,
|
||||
}
|
||||
|
||||
|
||||
def fuehre_in_eigenem_prozess_aus(quelltext: str) -> tuple[float, list[float]]:
|
||||
"""Startet den Codeschnipsel als separaten Python-Prozess und liest das Ergebnis."""
|
||||
programm = textwrap.dedent(quelltext) + "\nimport json; print(json.dumps(ausgabe))\n"
|
||||
ergebnis = subprocess.run([sys.executable, "-c", programm],
|
||||
capture_output=True, text=True, timeout=120)
|
||||
if ergebnis.returncode != 0:
|
||||
raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])
|
||||
wert, loesung = json.loads(ergebnis.stdout.strip().splitlines()[-1])
|
||||
return wert, loesung
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 78)
|
||||
print(" EIN SYSTEM - VIER ANSAETZE (je eigener Prozess)")
|
||||
|
|
@ -3225,14 +3259,20 @@ if __name__ == "__main__":
|
|||
print("-" * 78)
|
||||
|
||||
werte = []
|
||||
for name, quelltext in ANSAETZE.items():
|
||||
t0 = time.perf_counter()
|
||||
# Ein Pool, vier Aufgaben, vier frische Prozesse. Der Kontext muss
|
||||
# "spawn" sein - siehe Modulkommentar.
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
for name, funktion in ANSAETZE.items():
|
||||
beginn = time.perf_counter()
|
||||
try:
|
||||
wert, x = fuehre_in_eigenem_prozess_aus(quelltext)
|
||||
except RuntimeError as fehler:
|
||||
print(f"{name:<26} nicht verfuegbar: {fehler[:40]}")
|
||||
wert, x = pool.submit(funktion).result(timeout=120)
|
||||
except Exception as fehler: # Bibliothek fehlt o. Ae.
|
||||
print(f"{name:<26} nicht verfuegbar: {str(fehler)[:40]}")
|
||||
continue
|
||||
dauer = time.perf_counter() - t0
|
||||
dauer = time.perf_counter() - beginn
|
||||
werte.append(wert)
|
||||
print(f"{name:<26} {wert:>10.2f} {x[0]:>7.2f} {x[1]:>7.2f} {x[2]:>7.2f} "
|
||||
f"{dauer:>8.2f} s")
|
||||
|
|
@ -3245,8 +3285,8 @@ if __name__ == "__main__":
|
|||
assert spanne < 1e-6, "Die Bibliotheken widersprechen sich!"
|
||||
assert abs(werte[0] - ERWARTET) < 1e-6, "Ergebnis weicht von der Handrechnung ab!"
|
||||
print("Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.")
|
||||
print("(Die Zeiten enthalten den Prozessstart und den Import - sie messen")
|
||||
print(" NICHT die reine Solverleistung, siehe Uebung 3.5.)")
|
||||
print("(Die Zeiten enthalten Prozessstart und Import - sie messen NICHT die")
|
||||
print(" reine Solverleistung. Die Uebungsaufgabe 'Laufzeitvergleich' trennt beides.)")
|
||||
print("=" * 78)
|
||||
```
|
||||
|
||||
|
|
@ -3258,16 +3298,16 @@ if __name__ == "__main__":
|
|||
==============================================================================
|
||||
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.)
|
||||
==============================================================================
|
||||
```
|
||||
|
||||
|
|
@ -22663,9 +22703,9 @@ Benoetigt: numpy, pydantic, ortools, highspy (jeweils im eigenen Prozess)
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import multiprocessing
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
import numpy as np
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
|
@ -22868,25 +22908,29 @@ def geoeffnete_lager(problem: Standortproblem, loesung: Loesung) -> list[str]:
|
|||
if any(loesung.werte[problem.schluessel(i, j)] > 0.5 for j in range(m))]
|
||||
|
||||
|
||||
def loese_in_eigenem_prozess(name: str) -> Loesung:
|
||||
"""Startet dieses Programm noch einmal - mit genau einem Solverimport."""
|
||||
ergebnis = subprocess.run([sys.executable, __file__, name],
|
||||
capture_output=True, text=True, timeout=300)
|
||||
if ergebnis.returncode != 0:
|
||||
raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])
|
||||
# Das DTO als JSON - genau dafuer ist ein Datenobjekt ohne Solverbezug gut.
|
||||
return Loesung.model_validate_json(ergebnis.stdout.strip().splitlines()[-1])
|
||||
def loese_in_eigenem_prozess(name: str, problem: Standortproblem) -> Loesung:
|
||||
"""Laesst genau einen Modellbauer in einem frischen Prozess rechnen.
|
||||
|
||||
'spawn' statt des Linux-Standards 'fork': Der Kindprozess startet mit
|
||||
einem leeren Interpreter und importiert nur den Solver, den SEIN
|
||||
Modellbauer braucht. max_tasks_per_child=1 sorgt dafuer, dass der Pool
|
||||
seinen Arbeiter nicht wiederverwendet - sonst saessen beim zweiten Aufruf
|
||||
wieder beide Bibliotheken im selben Prozess.
|
||||
|
||||
Hin und zurueck wandert das Domaenenmodell bzw. das Loesungs-DTO. Beide
|
||||
kennen keinen Solver, sind also serialisierbar - genau dafuer sind sie da.
|
||||
"""
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
return pool.submit(MODELLBAUER[name], problem).result(timeout=300)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
problem = beispielproblem()
|
||||
|
||||
# --- Kindprozess: rechnen und das DTO als JSON ausgeben ---------------
|
||||
if len(sys.argv) > 1:
|
||||
print(MODELLBAUER[sys.argv[1]](problem).model_dump_json())
|
||||
sys.exit(0)
|
||||
|
||||
# --- Hauptprozess: beide Solver anstossen und vergleichen -------------
|
||||
# --- Beide Solver anstossen und vergleichen ---------------------------
|
||||
print("=" * 82)
|
||||
print(" DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE")
|
||||
print("=" * 82)
|
||||
|
|
@ -22898,7 +22942,7 @@ if __name__ == "__main__":
|
|||
loesungen: dict[str, Loesung] = {}
|
||||
for name, beschriftung in [("cpsat", "OR-Tools CP-SAT"),
|
||||
("highs", "HiGHS (highspy)")]:
|
||||
loesung = loesungen[name] = loese_in_eigenem_prozess(name)
|
||||
loesung = loesungen[name] = loese_in_eigenem_prozess(name, problem)
|
||||
beanstandungen = pruefe_zuordnung(problem, loesung)
|
||||
|
||||
print(f"{beschriftung}")
|
||||
|
|
@ -24161,113 +24205,124 @@ Benoetigt: numpy; in den Kindprozessen scipy, highspy, ortools, cvxpy
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import multiprocessing
|
||||
import resource
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
import numpy as np
|
||||
|
||||
GROESSEN = [(10, 10), (32, 32), (100, 100)] # (Lager, Kunden) -> 100 / 1.024 / 10.000 Variablen
|
||||
|
||||
|
||||
# Jeder Eintrag ist ein eigenstaendiges Programm: Instanz aufbauen, loesen,
|
||||
# Ergebnis als JSON ausgeben. Die Instanz wird in jedem Kindprozess aus
|
||||
# derselben Saat neu erzeugt - so reist nichts ueber die Prozessgrenze,
|
||||
# was das Ergebnis verfaelschen koennte.
|
||||
VORSPANN = """
|
||||
import json, time, resource
|
||||
import numpy as np
|
||||
# Instanz und Speichermessung stehen als gewoehnliche Funktionen hier - nicht
|
||||
# in einem String, den ein Kindprozess ausfuehrt. Jede Messfunktion baut die
|
||||
# Instanz aus derselben Saat neu auf, damit ueber die Prozessgrenze nichts
|
||||
# reist, was das Ergebnis verfaelschen koennte.
|
||||
|
||||
def instanz(m, n):
|
||||
def instanz(m: int, n: int):
|
||||
rng = np.random.default_rng(20)
|
||||
kosten = rng.integers(5, 95, (m, n)).astype(float)
|
||||
angebot = rng.integers(50, 150, m).astype(float)
|
||||
bedarf = angebot.sum() * rng.dirichlet(np.ones(n))
|
||||
return kosten, angebot, bedarf
|
||||
|
||||
def speicher_mb():
|
||||
# ru_maxrss ist unter Linux in Kilobyte
|
||||
|
||||
def speicher_mb() -> float:
|
||||
# ru_maxrss ist unter Linux in Kilobyte. Gemessen wird der Kindprozess -
|
||||
# deshalb muss jede Messung einen eigenen bekommen.
|
||||
return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024
|
||||
|
||||
M, N = {m}, {n}
|
||||
kosten, angebot, bedarf = instanz(M, N)
|
||||
"""
|
||||
|
||||
ANSAETZE = {
|
||||
"scipy.linprog": """
|
||||
def messe_scipy(m: int, n: int):
|
||||
from scipy.optimize import linprog
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
c = kosten.reshape(-1)
|
||||
A_ub = np.zeros((M, M * N)); A_eq = np.zeros((N, M * N))
|
||||
for i in range(M):
|
||||
A_ub[i, i * N:(i + 1) * N] = 1.0
|
||||
for j in range(N):
|
||||
A_eq[j, j::N] = 1.0
|
||||
A_ub = np.zeros((m, m * n)); A_eq = np.zeros((n, m * n))
|
||||
for i in range(m):
|
||||
A_ub[i, i * n:(i + 1) * n] = 1.0
|
||||
for j in range(n):
|
||||
A_eq[j, j::n] = 1.0
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter()
|
||||
r = linprog(c=c, A_ub=A_ub, b_ub=angebot, A_eq=A_eq, b_eq=bedarf,
|
||||
bounds=(0, None), method="highs")
|
||||
loesen = time.perf_counter() - t0
|
||||
ausgabe = (float(r.fun), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return float(r.fun), aufbau, loesen, speicher_mb()
|
||||
|
||||
"highspy": """
|
||||
|
||||
def messe_highspy(m: int, n: int):
|
||||
import highspy
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
h = highspy.Highs(); h.setOptionValue("output_flag", False)
|
||||
h.addVars(M * N, np.zeros(M * N), np.full(M * N, highspy.kHighsInf))
|
||||
for k in range(M * N):
|
||||
h.addVars(m * n, np.zeros(m * n), np.full(m * n, highspy.kHighsInf))
|
||||
for k in range(m * n):
|
||||
h.changeColCost(k, float(kosten.reshape(-1)[k]))
|
||||
for i in range(M):
|
||||
idx = np.arange(i * N, (i + 1) * N, dtype=np.int32)
|
||||
h.addRow(-highspy.kHighsInf, float(angebot[i]), N, idx, np.ones(N))
|
||||
for j in range(N):
|
||||
idx = np.arange(j, M * N, N, dtype=np.int32)
|
||||
h.addRow(float(bedarf[j]), float(bedarf[j]), M, idx, np.ones(M))
|
||||
for i in range(m):
|
||||
idx = np.arange(i * n, (i + 1) * n, dtype=np.int32)
|
||||
h.addRow(-highspy.kHighsInf, float(angebot[i]), n, idx, np.ones(n))
|
||||
for j in range(n):
|
||||
idx = np.arange(j, m * n, n, dtype=np.int32)
|
||||
h.addRow(float(bedarf[j]), float(bedarf[j]), m, idx, np.ones(m))
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); h.run(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (h.getInfo().objective_function_value, aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return h.getInfo().objective_function_value, aufbau, loesen, speicher_mb()
|
||||
|
||||
"ortools/GLOP": """
|
||||
|
||||
def messe_ortools(m: int, n: int):
|
||||
from ortools.linear_solver import pywraplp
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
s = pywraplp.Solver.CreateSolver("GLOP")
|
||||
x = [[s.NumVar(0, s.infinity(), f"x{i}_{j}") for j in range(N)]
|
||||
for i in range(M)]
|
||||
for i in range(M):
|
||||
x = [[s.NumVar(0, s.infinity(), f"x{i}_{j}") for j in range(n)]
|
||||
for i in range(m)]
|
||||
for i in range(m):
|
||||
s.Add(sum(x[i]) <= float(angebot[i]))
|
||||
for j in range(N):
|
||||
s.Add(sum(x[i][j] for i in range(M)) == float(bedarf[j]))
|
||||
for j in range(n):
|
||||
s.Add(sum(x[i][j] for i in range(m)) == float(bedarf[j]))
|
||||
s.Minimize(sum(float(kosten[i, j]) * x[i][j]
|
||||
for i in range(M) for j in range(N)))
|
||||
for i in range(m) for j in range(n)))
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); s.Solve(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (s.Objective().Value(), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return s.Objective().Value(), aufbau, loesen, speicher_mb()
|
||||
|
||||
"cvxpy": """
|
||||
|
||||
def messe_cvxpy(m: int, n: int):
|
||||
import cvxpy as cp
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
x = cp.Variable((M, N), nonneg=True)
|
||||
x = cp.Variable((m, n), nonneg=True)
|
||||
problem = cp.Problem(cp.Minimize(cp.sum(cp.multiply(kosten, x))),
|
||||
[cp.sum(x, axis=1) <= angebot,
|
||||
cp.sum(x, axis=0) == bedarf])
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); problem.solve(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (float(problem.value), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
}
|
||||
return float(problem.value), aufbau, loesen, speicher_mb()
|
||||
|
||||
|
||||
def messe(name: str, quelltext: str, m: int, n: int):
|
||||
"""Fuehrt einen Ansatz in einem eigenen Prozess aus."""
|
||||
programm = (VORSPANN.format(m=m, n=n) + textwrap.dedent(quelltext)
|
||||
+ "\nprint(json.dumps(ausgabe))\n")
|
||||
ergebnis = subprocess.run([sys.executable, "-c", programm],
|
||||
capture_output=True, text=True, timeout=600)
|
||||
if ergebnis.returncode != 0:
|
||||
return None, ergebnis.stderr.strip().splitlines()[-1][:60]
|
||||
return json.loads(ergebnis.stdout.strip().splitlines()[-1]), None
|
||||
ANSAETZE = {"scipy.linprog": messe_scipy, "highspy": messe_highspy,
|
||||
"ortools/GLOP": messe_ortools, "cvxpy": messe_cvxpy}
|
||||
|
||||
|
||||
def messe(funktion, m: int, n: int):
|
||||
"""Fuehrt eine Messfunktion in einem FRISCHEN Prozess aus.
|
||||
|
||||
'spawn' und max_tasks_per_child=1 zusammen garantieren, was Regel 1
|
||||
verlangt: Jede Messung sieht einen leeren Interpreter. Ohne das
|
||||
zweite wuerde der Pool seinen Arbeiter wiederverwenden - dann waere
|
||||
der Speicherwert der zweiten Bibliothek um die erste zu hoch, und
|
||||
ortools und highspy saessen im selben Prozess.
|
||||
"""
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
try:
|
||||
return pool.submit(funktion, m, n).result(timeout=600), None
|
||||
except Exception as fehler:
|
||||
return None, str(fehler).strip().splitlines()[-1][:60]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
@ -24285,8 +24340,8 @@ if __name__ == "__main__":
|
|||
f"{'Loesen':>9} {'Anteil':>8} {'Speicher':>10}")
|
||||
print(" " + "-" * 72)
|
||||
zielwerte = {}
|
||||
for name, quelltext in ANSAETZE.items():
|
||||
werte, fehler = messe(name, quelltext, m, n)
|
||||
for name, funktion in ANSAETZE.items():
|
||||
werte, fehler = messe(funktion, m, n)
|
||||
if werte is None:
|
||||
print(f" {name:<16} nicht verfuegbar: {fehler}")
|
||||
continue
|
||||
|
|
@ -24342,28 +24397,28 @@ die Zielwerte und ihr Verhaeltnis zueinander nicht.
|
|||
--- 10 Lager x 10 Kunden = 100 Variablen ---------------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 13,509.48 0.000s 0.004s 1% 78 MB
|
||||
highspy 13,509.48 0.001s 0.002s 34% 41 MB
|
||||
ortools/GLOP 13,509.48 0.002s 0.001s 80% 54 MB
|
||||
scipy.linprog 13,509.48 0.000s 0.004s 1% 79 MB
|
||||
highspy 13,509.48 0.001s 0.002s 36% 44 MB
|
||||
ortools/GLOP 13,509.48 0.003s 0.001s 80% 56 MB
|
||||
cvxpy 13,509.48 0.001s 0.009s 9% 229 MB
|
||||
Spannweite der Zielwerte: 1.33e-06 (relativ 9.8e-11)
|
||||
|
||||
--- 32 Lager x 32 Kunden = 1,024 Variablen -------------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 35,744.25 0.000s 0.009s 4% 80 MB
|
||||
highspy 35,744.25 0.004s 0.005s 47% 42 MB
|
||||
ortools/GLOP 35,744.25 0.015s 0.002s 85% 55 MB
|
||||
cvxpy 35,744.25 0.001s 0.016s 5% 230 MB
|
||||
scipy.linprog 35,744.25 0.000s 0.009s 3% 81 MB
|
||||
highspy 35,744.25 0.004s 0.005s 44% 45 MB
|
||||
ortools/GLOP 35,744.25 0.014s 0.002s 85% 58 MB
|
||||
cvxpy 35,744.25 0.001s 0.017s 5% 231 MB
|
||||
Spannweite der Zielwerte: 9.54e-05 (relativ 2.7e-09)
|
||||
|
||||
--- 100 Lager x 100 Kunden = 10,000 Variablen ----------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 72,220.34 0.002s 0.070s 3% 120 MB
|
||||
highspy 72,220.34 0.029s 0.034s 45% 47 MB
|
||||
ortools/GLOP 72,220.34 0.123s 0.036s 77% 66 MB
|
||||
cvxpy 72,220.34 0.001s 0.103s 1% 242 MB
|
||||
scipy.linprog 72,220.34 0.004s 0.072s 5% 122 MB
|
||||
highspy 72,220.34 0.026s 0.032s 45% 50 MB
|
||||
ortools/GLOP 72,220.34 0.141s 0.043s 77% 68 MB
|
||||
cvxpy 72,220.34 0.001s 0.108s 1% 242 MB
|
||||
Spannweite der Zielwerte: 3.65e-04 (relativ 5.0e-09)
|
||||
|
||||
============================================================================================
|
||||
|
|
@ -27863,7 +27918,7 @@ ImportError: .../highspy/_core...so: undefined symbol: _ZN5Highs13releaseMemoryE
|
|||
|
||||
**Abhilfen (in dieser Reihenfolge):**
|
||||
1. Nur eines von beiden im selben Skript verwenden.
|
||||
2. Getrennte Prozesse (`subprocess`) — siehe `Ein_System_Vier_Ansaetze.py`.
|
||||
2. Getrennte Prozesse — ein `ProcessPoolExecutor` mit `mp_context="spawn"` und `max_tasks_per_child=1`, siehe `Ein_System_Vier_Ansaetze.py`.
|
||||
3. Auf `highspy` verzichten: HiGHS ist ohnehin Backend von `scipy.optimize.linprog` und CVXPY.
|
||||
4. Getrennte virtuelle Umgebungen.
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -308,6 +308,34 @@ $$
|
|||
> prüft `highspy` und `cvxpy` in der Paketübersicht nur auf Anwesenheit
|
||||
> (`importlib.util.find_spec`) und importiert CVXPY erst im Funktionstest.
|
||||
|
||||
### Wie die Isolation aussieht, wenn sie tragen soll
|
||||
|
||||
„Eigener Prozess" ist schnell gesagt. Die naheliegende Umsetzung — ein Codeschnipsel als
|
||||
Zeichenkette an `python -c` ü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.
|
||||
|
||||
Tragfähig ist stattdessen: **jeder Solver eine gewöhnliche Funktion mit lokalem Import**,
|
||||
ausgeführt von einem `ProcessPoolExecutor` mit zwei Einstellungen, die zusammen die
|
||||
Garantie ergeben:
|
||||
|
||||
| Einstellung | Wozu |
|
||||
| --- | --- |
|
||||
| `mp_context=multiprocessing.get_context("spawn")` | Der Kindprozess startet mit einem **frischen** Interpreter, statt den Speicher des Elternprozesses zu erben. Unter Linux ist `fork` der Standard — und damit wäre alles, was hier schon importiert ist, auch dort importiert. |
|
||||
| `max_tasks_per_child=1` | Jede Aufgabe bekommt einen **neuen** 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. |
|
||||
|
||||
> **⚠️ `max_tasks_per_child=1` ist nicht optional**
|
||||
> Ein Pool ohne diese Angabe ist der **Normalfall** — 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.
|
||||
|
||||
Denselben Aufbau verwenden `Solverwechsel_CPSAT_HiGHS.py` ({ref:kap:praxisfallen}) und
|
||||
`Benchmark_Skalierung.py` ({ref:kap:testing}). Dort wandern zusätzlich **Datenobjekte** über
|
||||
die Prozessgrenze statt Zeichenketten — möglich, weil Domänenmodell und Lösungs-DTO keinen
|
||||
Solver kennen ({ref:sec:praxisfallen-or-kern}).
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
|
@ -319,85 +347,104 @@ Kapitel Oekosystem: Dasselbe LP in vier Bibliotheken.
|
|||
2*x1 + 3*x2 + x3 <= 50
|
||||
x >= 0
|
||||
|
||||
Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich am Ende.
|
||||
Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich
|
||||
am Ende.
|
||||
|
||||
WICHTIG: Jeder Solver läuft in einem EIGENEN Prozess, weil sich ortools und
|
||||
WICHTIG: Jeder Solver laeuft in einem EIGENEN Prozess, weil sich ortools und
|
||||
highspy auf vielen Systemen nicht gemeinsam importieren lassen (beide bringen
|
||||
eine eigene HiGHS-Kopie mit -> Symbolkonflikt).
|
||||
|
||||
Die Isolation besorgt ein ProcessPoolExecutor. Drei Einstellungen ergeben
|
||||
zusammen die Garantie:
|
||||
|
||||
mp_context "spawn" Der Kindprozess startet mit einem FRISCHEN
|
||||
Interpreter, statt den Speicher des Elternprozesses
|
||||
zu erben. Was hier schon importiert ist, ist dort
|
||||
nicht importiert. Mit dem Standard "fork" auf Linux
|
||||
waere das nicht so.
|
||||
max_tasks_per_child=1 Jede Aufgabe bekommt einen NEUEN Prozess. Ohne das
|
||||
wuerde der Pool seinen Arbeiter wiederverwenden - und
|
||||
beim zweiten Solver waere der Konflikt zurueck.
|
||||
max_workers=1 Haelt die vier Laeufe nacheinander. Nicht aus
|
||||
Vorsicht, sondern damit die gemessenen Zeiten
|
||||
vergleichbar bleiben.
|
||||
|
||||
Jeder Solver steht in einer eigenen Funktion mit LOKALEM Import. Das ist der
|
||||
Unterschied zu einem Codestring, den man an 'python -c' uebergibt: Die
|
||||
Funktion laesst sich einzeln aufrufen, testen und vom Editor pruefen - ein
|
||||
String nicht.
|
||||
|
||||
Benoetigt: scipy, highspy, cvxpy, ortools
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import multiprocessing
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
ERWARTET = 530.0 # Ergebnis der Handrechnung zum Produktionsprogramm
|
||||
|
||||
# Jeder Eintrag ist ein eigenständiges Miniprogramm, das sein Ergebnis als
|
||||
# JSON auf stdout ausgibt. So bleibt jeder Import in seinem eigenen Prozess.
|
||||
ANSAETZE: dict[str, str] = {
|
||||
# Die Instanz - einmal notiert, von allen vier Funktionen benutzt.
|
||||
ZIEL = [10.0, 15.0, 25.0]
|
||||
MATRIX = [[1, 1, 2], [2, 3, 1]]
|
||||
KAPAZITAET = [40.0, 50.0]
|
||||
|
||||
"scipy.optimize.linprog": """
|
||||
|
||||
def loese_mit_scipy() -> tuple[float, list[float]]:
|
||||
from scipy.optimize import linprog
|
||||
res = linprog(c=[-10.0, -15.0, -25.0], # linprog MINIMIERT -> negieren
|
||||
A_ub=[[1, 1, 2], [2, 3, 1]], b_ub=[40, 50],
|
||||
ergebnis = linprog(c=[-w for w in ZIEL], # linprog MINIMIERT -> negieren
|
||||
A_ub=MATRIX, b_ub=KAPAZITAET,
|
||||
bounds=[(0, None)] * 3, method="highs")
|
||||
ausgabe = (-res.fun, list(res.x))
|
||||
""",
|
||||
return -ergebnis.fun, list(ergebnis.x)
|
||||
|
||||
"highspy (natives HiGHS)": """
|
||||
import numpy as np, highspy
|
||||
|
||||
def loese_mit_highspy() -> tuple[float, list[float]]:
|
||||
import highspy
|
||||
import numpy as np
|
||||
h = highspy.Highs()
|
||||
h.setOptionValue("output_flag", False)
|
||||
h.addVars(3, np.zeros(3), np.full(3, highspy.kHighsInf))
|
||||
h.changeObjectiveSense(highspy.ObjSense.kMaximize)
|
||||
for j, wert in enumerate([10.0, 15.0, 25.0]):
|
||||
for j, wert in enumerate(ZIEL):
|
||||
h.changeColCost(j, wert)
|
||||
# CSR-Format: starts[i] = Beginn von Zeile i in indices/values
|
||||
h.addRows(2, np.full(2, -highspy.kHighsInf), np.array([40.0, 50.0]), 6,
|
||||
h.addRows(2, np.full(2, -highspy.kHighsInf), np.array(KAPAZITAET), 6,
|
||||
np.array([0, 3], dtype=np.int32),
|
||||
np.array([0, 1, 2, 0, 1, 2], dtype=np.int32),
|
||||
np.array([1.0, 1.0, 2.0, 2.0, 3.0, 1.0]))
|
||||
np.array([float(w) for zeile in MATRIX for w in zeile]))
|
||||
h.run()
|
||||
ausgabe = (h.getInfo().objective_function_value,
|
||||
return (h.getInfo().objective_function_value,
|
||||
list(h.getSolution().col_value[:3]))
|
||||
""",
|
||||
|
||||
"cvxpy": """
|
||||
import numpy as np, cvxpy as cp
|
||||
|
||||
def loese_mit_cvxpy() -> tuple[float, list[float]]:
|
||||
import cvxpy as cp
|
||||
import numpy as np
|
||||
x = cp.Variable(3, nonneg=True)
|
||||
problem = cp.Problem(cp.Maximize(np.array([10.0, 15.0, 25.0]) @ x),
|
||||
[np.array([[1, 1, 2], [2, 3, 1]]) @ x <= np.array([40, 50])])
|
||||
problem = cp.Problem(cp.Maximize(np.array(ZIEL) @ x),
|
||||
[np.array(MATRIX) @ x <= np.array(KAPAZITAET)])
|
||||
problem.solve()
|
||||
ausgabe = (float(problem.value), [float(v) for v in x.value])
|
||||
""",
|
||||
return float(problem.value), [float(v) for v in x.value]
|
||||
|
||||
"ortools / GLOP": """
|
||||
|
||||
def loese_mit_ortools() -> tuple[float, list[float]]:
|
||||
from ortools.linear_solver import pywraplp
|
||||
s = pywraplp.Solver.CreateSolver("GLOP")
|
||||
x = [s.NumVar(0, s.infinity(), f"x{j+1}") for j in range(3)]
|
||||
A = [[1, 1, 2], [2, 3, 1]]
|
||||
for i, kap in enumerate([40, 50]):
|
||||
s.Add(sum(A[i][j] * x[j] for j in range(3)) <= kap)
|
||||
s.Maximize(10 * x[0] + 15 * x[1] + 25 * x[2])
|
||||
for i, kapazitaet in enumerate(KAPAZITAET):
|
||||
s.Add(sum(MATRIX[i][j] * x[j] for j in range(3)) <= kapazitaet)
|
||||
s.Maximize(sum(ZIEL[j] * x[j] for j in range(3)))
|
||||
s.Solve()
|
||||
ausgabe = (s.Objective().Value(), [v.solution_value() for v in x])
|
||||
""",
|
||||
return s.Objective().Value(), [v.solution_value() for v in x]
|
||||
|
||||
|
||||
ANSAETZE = {
|
||||
"scipy.optimize.linprog": loese_mit_scipy,
|
||||
"highspy (natives HiGHS)": loese_mit_highspy,
|
||||
"cvxpy": loese_mit_cvxpy,
|
||||
"ortools / GLOP": loese_mit_ortools,
|
||||
}
|
||||
|
||||
|
||||
def fuehre_in_eigenem_prozess_aus(quelltext: str) -> tuple[float, list[float]]:
|
||||
"""Startet den Codeschnipsel als separaten Python-Prozess und liest das Ergebnis."""
|
||||
programm = textwrap.dedent(quelltext) + "\nimport json; print(json.dumps(ausgabe))\n"
|
||||
ergebnis = subprocess.run([sys.executable, "-c", programm],
|
||||
capture_output=True, text=True, timeout=120)
|
||||
if ergebnis.returncode != 0:
|
||||
raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])
|
||||
wert, loesung = json.loads(ergebnis.stdout.strip().splitlines()[-1])
|
||||
return wert, loesung
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 78)
|
||||
print(" EIN SYSTEM - VIER ANSAETZE (je eigener Prozess)")
|
||||
|
|
@ -406,14 +453,20 @@ if __name__ == "__main__":
|
|||
print("-" * 78)
|
||||
|
||||
werte = []
|
||||
for name, quelltext in ANSAETZE.items():
|
||||
t0 = time.perf_counter()
|
||||
# Ein Pool, vier Aufgaben, vier frische Prozesse. Der Kontext muss
|
||||
# "spawn" sein - siehe Modulkommentar.
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
for name, funktion in ANSAETZE.items():
|
||||
beginn = time.perf_counter()
|
||||
try:
|
||||
wert, x = fuehre_in_eigenem_prozess_aus(quelltext)
|
||||
except RuntimeError as fehler:
|
||||
print(f"{name:<26} nicht verfuegbar: {fehler[:40]}")
|
||||
wert, x = pool.submit(funktion).result(timeout=120)
|
||||
except Exception as fehler: # Bibliothek fehlt o. Ae.
|
||||
print(f"{name:<26} nicht verfuegbar: {str(fehler)[:40]}")
|
||||
continue
|
||||
dauer = time.perf_counter() - t0
|
||||
dauer = time.perf_counter() - beginn
|
||||
werte.append(wert)
|
||||
print(f"{name:<26} {wert:>10.2f} {x[0]:>7.2f} {x[1]:>7.2f} {x[2]:>7.2f} "
|
||||
f"{dauer:>8.2f} s")
|
||||
|
|
@ -426,8 +479,8 @@ if __name__ == "__main__":
|
|||
assert spanne < 1e-6, "Die Bibliotheken widersprechen sich!"
|
||||
assert abs(werte[0] - ERWARTET) < 1e-6, "Ergebnis weicht von der Handrechnung ab!"
|
||||
print("Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.")
|
||||
print("(Die Zeiten enthalten den Prozessstart und den Import - sie messen")
|
||||
print(" NICHT die reine Solverleistung, siehe Uebung 3.5.)")
|
||||
print("(Die Zeiten enthalten Prozessstart und Import - sie messen NICHT die")
|
||||
print(" reine Solverleistung. Die Uebungsaufgabe 'Laufzeitvergleich' trennt beides.)")
|
||||
print("=" * 78)
|
||||
```
|
||||
|
||||
|
|
@ -439,16 +492,16 @@ if __name__ == "__main__":
|
|||
==============================================================================
|
||||
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.)
|
||||
==============================================================================
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -1666,9 +1666,9 @@ Benoetigt: numpy, pydantic, ortools, highspy (jeweils im eigenen Prozess)
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import multiprocessing
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
import numpy as np
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
|
@ -1871,25 +1871,29 @@ def geoeffnete_lager(problem: Standortproblem, loesung: Loesung) -> list[str]:
|
|||
if any(loesung.werte[problem.schluessel(i, j)] > 0.5 for j in range(m))]
|
||||
|
||||
|
||||
def loese_in_eigenem_prozess(name: str) -> Loesung:
|
||||
"""Startet dieses Programm noch einmal - mit genau einem Solverimport."""
|
||||
ergebnis = subprocess.run([sys.executable, __file__, name],
|
||||
capture_output=True, text=True, timeout=300)
|
||||
if ergebnis.returncode != 0:
|
||||
raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])
|
||||
# Das DTO als JSON - genau dafuer ist ein Datenobjekt ohne Solverbezug gut.
|
||||
return Loesung.model_validate_json(ergebnis.stdout.strip().splitlines()[-1])
|
||||
def loese_in_eigenem_prozess(name: str, problem: Standortproblem) -> Loesung:
|
||||
"""Laesst genau einen Modellbauer in einem frischen Prozess rechnen.
|
||||
|
||||
'spawn' statt des Linux-Standards 'fork': Der Kindprozess startet mit
|
||||
einem leeren Interpreter und importiert nur den Solver, den SEIN
|
||||
Modellbauer braucht. max_tasks_per_child=1 sorgt dafuer, dass der Pool
|
||||
seinen Arbeiter nicht wiederverwendet - sonst saessen beim zweiten Aufruf
|
||||
wieder beide Bibliotheken im selben Prozess.
|
||||
|
||||
Hin und zurueck wandert das Domaenenmodell bzw. das Loesungs-DTO. Beide
|
||||
kennen keinen Solver, sind also serialisierbar - genau dafuer sind sie da.
|
||||
"""
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
return pool.submit(MODELLBAUER[name], problem).result(timeout=300)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
problem = beispielproblem()
|
||||
|
||||
# --- Kindprozess: rechnen und das DTO als JSON ausgeben ---------------
|
||||
if len(sys.argv) > 1:
|
||||
print(MODELLBAUER[sys.argv[1]](problem).model_dump_json())
|
||||
sys.exit(0)
|
||||
|
||||
# --- Hauptprozess: beide Solver anstossen und vergleichen -------------
|
||||
# --- Beide Solver anstossen und vergleichen ---------------------------
|
||||
print("=" * 82)
|
||||
print(" DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE")
|
||||
print("=" * 82)
|
||||
|
|
@ -1901,7 +1905,7 @@ if __name__ == "__main__":
|
|||
loesungen: dict[str, Loesung] = {}
|
||||
for name, beschriftung in [("cpsat", "OR-Tools CP-SAT"),
|
||||
("highs", "HiGHS (highspy)")]:
|
||||
loesung = loesungen[name] = loese_in_eigenem_prozess(name)
|
||||
loesung = loesungen[name] = loese_in_eigenem_prozess(name, problem)
|
||||
beanstandungen = pruefe_zuordnung(problem, loesung)
|
||||
|
||||
print(f"{beschriftung}")
|
||||
|
|
|
|||
|
|
@ -787,113 +787,124 @@ Benoetigt: numpy; in den Kindprozessen scipy, highspy, ortools, cvxpy
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import multiprocessing
|
||||
import resource
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
import numpy as np
|
||||
|
||||
GROESSEN = [(10, 10), (32, 32), (100, 100)] # (Lager, Kunden) -> 100 / 1.024 / 10.000 Variablen
|
||||
|
||||
|
||||
# Jeder Eintrag ist ein eigenstaendiges Programm: Instanz aufbauen, loesen,
|
||||
# Ergebnis als JSON ausgeben. Die Instanz wird in jedem Kindprozess aus
|
||||
# derselben Saat neu erzeugt - so reist nichts ueber die Prozessgrenze,
|
||||
# was das Ergebnis verfaelschen koennte.
|
||||
VORSPANN = """
|
||||
import json, time, resource
|
||||
import numpy as np
|
||||
# Instanz und Speichermessung stehen als gewoehnliche Funktionen hier - nicht
|
||||
# in einem String, den ein Kindprozess ausfuehrt. Jede Messfunktion baut die
|
||||
# Instanz aus derselben Saat neu auf, damit ueber die Prozessgrenze nichts
|
||||
# reist, was das Ergebnis verfaelschen koennte.
|
||||
|
||||
def instanz(m, n):
|
||||
def instanz(m: int, n: int):
|
||||
rng = np.random.default_rng(20)
|
||||
kosten = rng.integers(5, 95, (m, n)).astype(float)
|
||||
angebot = rng.integers(50, 150, m).astype(float)
|
||||
bedarf = angebot.sum() * rng.dirichlet(np.ones(n))
|
||||
return kosten, angebot, bedarf
|
||||
|
||||
def speicher_mb():
|
||||
# ru_maxrss ist unter Linux in Kilobyte
|
||||
|
||||
def speicher_mb() -> float:
|
||||
# ru_maxrss ist unter Linux in Kilobyte. Gemessen wird der Kindprozess -
|
||||
# deshalb muss jede Messung einen eigenen bekommen.
|
||||
return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024
|
||||
|
||||
M, N = {m}, {n}
|
||||
kosten, angebot, bedarf = instanz(M, N)
|
||||
"""
|
||||
|
||||
ANSAETZE = {
|
||||
"scipy.linprog": """
|
||||
def messe_scipy(m: int, n: int):
|
||||
from scipy.optimize import linprog
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
c = kosten.reshape(-1)
|
||||
A_ub = np.zeros((M, M * N)); A_eq = np.zeros((N, M * N))
|
||||
for i in range(M):
|
||||
A_ub[i, i * N:(i + 1) * N] = 1.0
|
||||
for j in range(N):
|
||||
A_eq[j, j::N] = 1.0
|
||||
A_ub = np.zeros((m, m * n)); A_eq = np.zeros((n, m * n))
|
||||
for i in range(m):
|
||||
A_ub[i, i * n:(i + 1) * n] = 1.0
|
||||
for j in range(n):
|
||||
A_eq[j, j::n] = 1.0
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter()
|
||||
r = linprog(c=c, A_ub=A_ub, b_ub=angebot, A_eq=A_eq, b_eq=bedarf,
|
||||
bounds=(0, None), method="highs")
|
||||
loesen = time.perf_counter() - t0
|
||||
ausgabe = (float(r.fun), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return float(r.fun), aufbau, loesen, speicher_mb()
|
||||
|
||||
"highspy": """
|
||||
|
||||
def messe_highspy(m: int, n: int):
|
||||
import highspy
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
h = highspy.Highs(); h.setOptionValue("output_flag", False)
|
||||
h.addVars(M * N, np.zeros(M * N), np.full(M * N, highspy.kHighsInf))
|
||||
for k in range(M * N):
|
||||
h.addVars(m * n, np.zeros(m * n), np.full(m * n, highspy.kHighsInf))
|
||||
for k in range(m * n):
|
||||
h.changeColCost(k, float(kosten.reshape(-1)[k]))
|
||||
for i in range(M):
|
||||
idx = np.arange(i * N, (i + 1) * N, dtype=np.int32)
|
||||
h.addRow(-highspy.kHighsInf, float(angebot[i]), N, idx, np.ones(N))
|
||||
for j in range(N):
|
||||
idx = np.arange(j, M * N, N, dtype=np.int32)
|
||||
h.addRow(float(bedarf[j]), float(bedarf[j]), M, idx, np.ones(M))
|
||||
for i in range(m):
|
||||
idx = np.arange(i * n, (i + 1) * n, dtype=np.int32)
|
||||
h.addRow(-highspy.kHighsInf, float(angebot[i]), n, idx, np.ones(n))
|
||||
for j in range(n):
|
||||
idx = np.arange(j, m * n, n, dtype=np.int32)
|
||||
h.addRow(float(bedarf[j]), float(bedarf[j]), m, idx, np.ones(m))
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); h.run(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (h.getInfo().objective_function_value, aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return h.getInfo().objective_function_value, aufbau, loesen, speicher_mb()
|
||||
|
||||
"ortools/GLOP": """
|
||||
|
||||
def messe_ortools(m: int, n: int):
|
||||
from ortools.linear_solver import pywraplp
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
s = pywraplp.Solver.CreateSolver("GLOP")
|
||||
x = [[s.NumVar(0, s.infinity(), f"x{i}_{j}") for j in range(N)]
|
||||
for i in range(M)]
|
||||
for i in range(M):
|
||||
x = [[s.NumVar(0, s.infinity(), f"x{i}_{j}") for j in range(n)]
|
||||
for i in range(m)]
|
||||
for i in range(m):
|
||||
s.Add(sum(x[i]) <= float(angebot[i]))
|
||||
for j in range(N):
|
||||
s.Add(sum(x[i][j] for i in range(M)) == float(bedarf[j]))
|
||||
for j in range(n):
|
||||
s.Add(sum(x[i][j] for i in range(m)) == float(bedarf[j]))
|
||||
s.Minimize(sum(float(kosten[i, j]) * x[i][j]
|
||||
for i in range(M) for j in range(N)))
|
||||
for i in range(m) for j in range(n)))
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); s.Solve(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (s.Objective().Value(), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return s.Objective().Value(), aufbau, loesen, speicher_mb()
|
||||
|
||||
"cvxpy": """
|
||||
|
||||
def messe_cvxpy(m: int, n: int):
|
||||
import cvxpy as cp
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
x = cp.Variable((M, N), nonneg=True)
|
||||
x = cp.Variable((m, n), nonneg=True)
|
||||
problem = cp.Problem(cp.Minimize(cp.sum(cp.multiply(kosten, x))),
|
||||
[cp.sum(x, axis=1) <= angebot,
|
||||
cp.sum(x, axis=0) == bedarf])
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); problem.solve(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (float(problem.value), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
}
|
||||
return float(problem.value), aufbau, loesen, speicher_mb()
|
||||
|
||||
|
||||
def messe(name: str, quelltext: str, m: int, n: int):
|
||||
"""Fuehrt einen Ansatz in einem eigenen Prozess aus."""
|
||||
programm = (VORSPANN.format(m=m, n=n) + textwrap.dedent(quelltext)
|
||||
+ "\nprint(json.dumps(ausgabe))\n")
|
||||
ergebnis = subprocess.run([sys.executable, "-c", programm],
|
||||
capture_output=True, text=True, timeout=600)
|
||||
if ergebnis.returncode != 0:
|
||||
return None, ergebnis.stderr.strip().splitlines()[-1][:60]
|
||||
return json.loads(ergebnis.stdout.strip().splitlines()[-1]), None
|
||||
ANSAETZE = {"scipy.linprog": messe_scipy, "highspy": messe_highspy,
|
||||
"ortools/GLOP": messe_ortools, "cvxpy": messe_cvxpy}
|
||||
|
||||
|
||||
def messe(funktion, m: int, n: int):
|
||||
"""Fuehrt eine Messfunktion in einem FRISCHEN Prozess aus.
|
||||
|
||||
'spawn' und max_tasks_per_child=1 zusammen garantieren, was Regel 1
|
||||
verlangt: Jede Messung sieht einen leeren Interpreter. Ohne das
|
||||
zweite wuerde der Pool seinen Arbeiter wiederverwenden - dann waere
|
||||
der Speicherwert der zweiten Bibliothek um die erste zu hoch, und
|
||||
ortools und highspy saessen im selben Prozess.
|
||||
"""
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
try:
|
||||
return pool.submit(funktion, m, n).result(timeout=600), None
|
||||
except Exception as fehler:
|
||||
return None, str(fehler).strip().splitlines()[-1][:60]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
@ -911,8 +922,8 @@ if __name__ == "__main__":
|
|||
f"{'Loesen':>9} {'Anteil':>8} {'Speicher':>10}")
|
||||
print(" " + "-" * 72)
|
||||
zielwerte = {}
|
||||
for name, quelltext in ANSAETZE.items():
|
||||
werte, fehler = messe(name, quelltext, m, n)
|
||||
for name, funktion in ANSAETZE.items():
|
||||
werte, fehler = messe(funktion, m, n)
|
||||
if werte is None:
|
||||
print(f" {name:<16} nicht verfuegbar: {fehler}")
|
||||
continue
|
||||
|
|
@ -968,28 +979,28 @@ die Zielwerte und ihr Verhaeltnis zueinander nicht.
|
|||
--- 10 Lager x 10 Kunden = 100 Variablen ---------------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 13,509.48 0.000s 0.004s 1% 78 MB
|
||||
highspy 13,509.48 0.001s 0.002s 34% 41 MB
|
||||
ortools/GLOP 13,509.48 0.002s 0.001s 80% 54 MB
|
||||
scipy.linprog 13,509.48 0.000s 0.004s 1% 79 MB
|
||||
highspy 13,509.48 0.001s 0.002s 36% 44 MB
|
||||
ortools/GLOP 13,509.48 0.003s 0.001s 80% 56 MB
|
||||
cvxpy 13,509.48 0.001s 0.009s 9% 229 MB
|
||||
Spannweite der Zielwerte: 1.33e-06 (relativ 9.8e-11)
|
||||
|
||||
--- 32 Lager x 32 Kunden = 1,024 Variablen -------------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 35,744.25 0.000s 0.009s 4% 80 MB
|
||||
highspy 35,744.25 0.004s 0.005s 47% 42 MB
|
||||
ortools/GLOP 35,744.25 0.015s 0.002s 85% 55 MB
|
||||
cvxpy 35,744.25 0.001s 0.016s 5% 230 MB
|
||||
scipy.linprog 35,744.25 0.000s 0.009s 3% 81 MB
|
||||
highspy 35,744.25 0.004s 0.005s 44% 45 MB
|
||||
ortools/GLOP 35,744.25 0.014s 0.002s 85% 58 MB
|
||||
cvxpy 35,744.25 0.001s 0.017s 5% 231 MB
|
||||
Spannweite der Zielwerte: 9.54e-05 (relativ 2.7e-09)
|
||||
|
||||
--- 100 Lager x 100 Kunden = 10,000 Variablen ----------------------------------------------
|
||||
Bibliothek Zielwert Aufbau Loesen Anteil Speicher
|
||||
------------------------------------------------------------------------
|
||||
scipy.linprog 72,220.34 0.002s 0.070s 3% 120 MB
|
||||
highspy 72,220.34 0.029s 0.034s 45% 47 MB
|
||||
ortools/GLOP 72,220.34 0.123s 0.036s 77% 66 MB
|
||||
cvxpy 72,220.34 0.001s 0.103s 1% 242 MB
|
||||
scipy.linprog 72,220.34 0.004s 0.072s 5% 122 MB
|
||||
highspy 72,220.34 0.026s 0.032s 45% 50 MB
|
||||
ortools/GLOP 72,220.34 0.141s 0.043s 77% 68 MB
|
||||
cvxpy 72,220.34 0.001s 0.108s 1% 242 MB
|
||||
Spannweite der Zielwerte: 3.65e-04 (relativ 5.0e-09)
|
||||
|
||||
============================================================================================
|
||||
|
|
|
|||
|
|
@ -723,7 +723,8 @@ importiert, crasht daher mit derselben Meldung.
|
|||
|
||||
**Abhilfen (in dieser Reihenfolge):**
|
||||
1. Nur eines von beiden im selben Skript verwenden.
|
||||
2. Getrennte Prozesse (`subprocess`) — siehe `Ein_System_Vier_Ansaetze.py`.
|
||||
2. Getrennte Prozesse — ein `ProcessPoolExecutor` mit `mp_context="spawn"` und
|
||||
`max_tasks_per_child=1`, siehe `Ein_System_Vier_Ansaetze.py`.
|
||||
3. Auf `highspy` verzichten: HiGHS ist ohnehin Backend von `scipy.optimize.linprog` und
|
||||
CVXPY.
|
||||
4. Getrennte virtuelle Umgebungen.
|
||||
|
|
|
|||
|
|
@ -313,6 +313,20 @@ def pruefe_dateien() -> list[str]:
|
|||
f"{marken} Loesungen, das Kapitel aber {erwartet} Aufgaben.")
|
||||
fehlend.append(anhang)
|
||||
|
||||
# Ein {#sec:...}-Label an einer ###-Ueberschrift. ABSCHNITT_RE erkennt nur
|
||||
# '## ' - ein solches Label wird also NIE registriert, und jeder
|
||||
# {ref:...} darauf laeuft ins Leere. Der Fehler sieht dabei voellig
|
||||
# harmlos aus, weil die Ueberschrift richtig gesetzt wird.
|
||||
tiefes_label_re = re.compile(r"^#{3,} .*\{#sec:[\w-]+\}", re.MULTILINE)
|
||||
for name in DATEIEN:
|
||||
with open(os.path.join(HIER, name), encoding="utf-8") as f:
|
||||
inhalt = f.read()
|
||||
for treffer in tiefes_label_re.finditer(inhalt):
|
||||
zeile = inhalt[:treffer.start()].count("\n") + 1
|
||||
print(f"FEHLER: {name}:{zeile} haengt ein {{#sec:...}}-Label an eine "
|
||||
f"###-Ueberschrift - registriert werden nur '## '-Abschnitte.")
|
||||
fehlend.append(name)
|
||||
|
||||
# Die Lesekette. Jede Datei ausser der letzten schliesst mit
|
||||
# '*Weiter mit:* [...](naechste_datei.md)' - und zwar auf die Datei, die in
|
||||
# DATEIEN als naechste steht.
|
||||
|
|
|
|||
|
|
@ -33,113 +33,124 @@ Benoetigt: numpy; in den Kindprozessen scipy, highspy, ortools, cvxpy
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import multiprocessing
|
||||
import resource
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
import numpy as np
|
||||
|
||||
GROESSEN = [(10, 10), (32, 32), (100, 100)] # (Lager, Kunden) -> 100 / 1.024 / 10.000 Variablen
|
||||
|
||||
|
||||
# Jeder Eintrag ist ein eigenstaendiges Programm: Instanz aufbauen, loesen,
|
||||
# Ergebnis als JSON ausgeben. Die Instanz wird in jedem Kindprozess aus
|
||||
# derselben Saat neu erzeugt - so reist nichts ueber die Prozessgrenze,
|
||||
# was das Ergebnis verfaelschen koennte.
|
||||
VORSPANN = """
|
||||
import json, time, resource
|
||||
import numpy as np
|
||||
# Instanz und Speichermessung stehen als gewoehnliche Funktionen hier - nicht
|
||||
# in einem String, den ein Kindprozess ausfuehrt. Jede Messfunktion baut die
|
||||
# Instanz aus derselben Saat neu auf, damit ueber die Prozessgrenze nichts
|
||||
# reist, was das Ergebnis verfaelschen koennte.
|
||||
|
||||
def instanz(m, n):
|
||||
def instanz(m: int, n: int):
|
||||
rng = np.random.default_rng(20)
|
||||
kosten = rng.integers(5, 95, (m, n)).astype(float)
|
||||
angebot = rng.integers(50, 150, m).astype(float)
|
||||
bedarf = angebot.sum() * rng.dirichlet(np.ones(n))
|
||||
return kosten, angebot, bedarf
|
||||
|
||||
def speicher_mb():
|
||||
# ru_maxrss ist unter Linux in Kilobyte
|
||||
|
||||
def speicher_mb() -> float:
|
||||
# ru_maxrss ist unter Linux in Kilobyte. Gemessen wird der Kindprozess -
|
||||
# deshalb muss jede Messung einen eigenen bekommen.
|
||||
return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024
|
||||
|
||||
M, N = {m}, {n}
|
||||
kosten, angebot, bedarf = instanz(M, N)
|
||||
"""
|
||||
|
||||
ANSAETZE = {
|
||||
"scipy.linprog": """
|
||||
def messe_scipy(m: int, n: int):
|
||||
from scipy.optimize import linprog
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
c = kosten.reshape(-1)
|
||||
A_ub = np.zeros((M, M * N)); A_eq = np.zeros((N, M * N))
|
||||
for i in range(M):
|
||||
A_ub[i, i * N:(i + 1) * N] = 1.0
|
||||
for j in range(N):
|
||||
A_eq[j, j::N] = 1.0
|
||||
A_ub = np.zeros((m, m * n)); A_eq = np.zeros((n, m * n))
|
||||
for i in range(m):
|
||||
A_ub[i, i * n:(i + 1) * n] = 1.0
|
||||
for j in range(n):
|
||||
A_eq[j, j::n] = 1.0
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter()
|
||||
r = linprog(c=c, A_ub=A_ub, b_ub=angebot, A_eq=A_eq, b_eq=bedarf,
|
||||
bounds=(0, None), method="highs")
|
||||
loesen = time.perf_counter() - t0
|
||||
ausgabe = (float(r.fun), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return float(r.fun), aufbau, loesen, speicher_mb()
|
||||
|
||||
"highspy": """
|
||||
|
||||
def messe_highspy(m: int, n: int):
|
||||
import highspy
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
h = highspy.Highs(); h.setOptionValue("output_flag", False)
|
||||
h.addVars(M * N, np.zeros(M * N), np.full(M * N, highspy.kHighsInf))
|
||||
for k in range(M * N):
|
||||
h.addVars(m * n, np.zeros(m * n), np.full(m * n, highspy.kHighsInf))
|
||||
for k in range(m * n):
|
||||
h.changeColCost(k, float(kosten.reshape(-1)[k]))
|
||||
for i in range(M):
|
||||
idx = np.arange(i * N, (i + 1) * N, dtype=np.int32)
|
||||
h.addRow(-highspy.kHighsInf, float(angebot[i]), N, idx, np.ones(N))
|
||||
for j in range(N):
|
||||
idx = np.arange(j, M * N, N, dtype=np.int32)
|
||||
h.addRow(float(bedarf[j]), float(bedarf[j]), M, idx, np.ones(M))
|
||||
for i in range(m):
|
||||
idx = np.arange(i * n, (i + 1) * n, dtype=np.int32)
|
||||
h.addRow(-highspy.kHighsInf, float(angebot[i]), n, idx, np.ones(n))
|
||||
for j in range(n):
|
||||
idx = np.arange(j, m * n, n, dtype=np.int32)
|
||||
h.addRow(float(bedarf[j]), float(bedarf[j]), m, idx, np.ones(m))
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); h.run(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (h.getInfo().objective_function_value, aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return h.getInfo().objective_function_value, aufbau, loesen, speicher_mb()
|
||||
|
||||
"ortools/GLOP": """
|
||||
|
||||
def messe_ortools(m: int, n: int):
|
||||
from ortools.linear_solver import pywraplp
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
s = pywraplp.Solver.CreateSolver("GLOP")
|
||||
x = [[s.NumVar(0, s.infinity(), f"x{i}_{j}") for j in range(N)]
|
||||
for i in range(M)]
|
||||
for i in range(M):
|
||||
x = [[s.NumVar(0, s.infinity(), f"x{i}_{j}") for j in range(n)]
|
||||
for i in range(m)]
|
||||
for i in range(m):
|
||||
s.Add(sum(x[i]) <= float(angebot[i]))
|
||||
for j in range(N):
|
||||
s.Add(sum(x[i][j] for i in range(M)) == float(bedarf[j]))
|
||||
for j in range(n):
|
||||
s.Add(sum(x[i][j] for i in range(m)) == float(bedarf[j]))
|
||||
s.Minimize(sum(float(kosten[i, j]) * x[i][j]
|
||||
for i in range(M) for j in range(N)))
|
||||
for i in range(m) for j in range(n)))
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); s.Solve(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (s.Objective().Value(), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
return s.Objective().Value(), aufbau, loesen, speicher_mb()
|
||||
|
||||
"cvxpy": """
|
||||
|
||||
def messe_cvxpy(m: int, n: int):
|
||||
import cvxpy as cp
|
||||
kosten, angebot, bedarf = instanz(m, n)
|
||||
t0 = time.perf_counter()
|
||||
x = cp.Variable((M, N), nonneg=True)
|
||||
x = cp.Variable((m, n), nonneg=True)
|
||||
problem = cp.Problem(cp.Minimize(cp.sum(cp.multiply(kosten, x))),
|
||||
[cp.sum(x, axis=1) <= angebot,
|
||||
cp.sum(x, axis=0) == bedarf])
|
||||
aufbau = time.perf_counter() - t0
|
||||
t0 = time.perf_counter(); problem.solve(); loesen = time.perf_counter() - t0
|
||||
ausgabe = (float(problem.value), aufbau, loesen, speicher_mb())
|
||||
""",
|
||||
}
|
||||
return float(problem.value), aufbau, loesen, speicher_mb()
|
||||
|
||||
|
||||
def messe(name: str, quelltext: str, m: int, n: int):
|
||||
"""Fuehrt einen Ansatz in einem eigenen Prozess aus."""
|
||||
programm = (VORSPANN.format(m=m, n=n) + textwrap.dedent(quelltext)
|
||||
+ "\nprint(json.dumps(ausgabe))\n")
|
||||
ergebnis = subprocess.run([sys.executable, "-c", programm],
|
||||
capture_output=True, text=True, timeout=600)
|
||||
if ergebnis.returncode != 0:
|
||||
return None, ergebnis.stderr.strip().splitlines()[-1][:60]
|
||||
return json.loads(ergebnis.stdout.strip().splitlines()[-1]), None
|
||||
ANSAETZE = {"scipy.linprog": messe_scipy, "highspy": messe_highspy,
|
||||
"ortools/GLOP": messe_ortools, "cvxpy": messe_cvxpy}
|
||||
|
||||
|
||||
def messe(funktion, m: int, n: int):
|
||||
"""Fuehrt eine Messfunktion in einem FRISCHEN Prozess aus.
|
||||
|
||||
'spawn' und max_tasks_per_child=1 zusammen garantieren, was Regel 1
|
||||
verlangt: Jede Messung sieht einen leeren Interpreter. Ohne das
|
||||
zweite wuerde der Pool seinen Arbeiter wiederverwenden - dann waere
|
||||
der Speicherwert der zweiten Bibliothek um die erste zu hoch, und
|
||||
ortools und highspy saessen im selben Prozess.
|
||||
"""
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
try:
|
||||
return pool.submit(funktion, m, n).result(timeout=600), None
|
||||
except Exception as fehler:
|
||||
return None, str(fehler).strip().splitlines()[-1][:60]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
@ -157,8 +168,8 @@ if __name__ == "__main__":
|
|||
f"{'Loesen':>9} {'Anteil':>8} {'Speicher':>10}")
|
||||
print(" " + "-" * 72)
|
||||
zielwerte = {}
|
||||
for name, quelltext in ANSAETZE.items():
|
||||
werte, fehler = messe(name, quelltext, m, n)
|
||||
for name, funktion in ANSAETZE.items():
|
||||
werte, fehler = messe(funktion, m, n)
|
||||
if werte is None:
|
||||
print(f" {name:<16} nicht verfuegbar: {fehler}")
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -8,85 +8,104 @@ Kapitel Oekosystem: Dasselbe LP in vier Bibliotheken.
|
|||
2*x1 + 3*x2 + x3 <= 50
|
||||
x >= 0
|
||||
|
||||
Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich am Ende.
|
||||
Deckt scipy.optimize, highspy, CVXPY und OR-Tools/GLOP ab, mit Kreuzvergleich
|
||||
am Ende.
|
||||
|
||||
WICHTIG: Jeder Solver läuft in einem EIGENEN Prozess, weil sich ortools und
|
||||
WICHTIG: Jeder Solver laeuft in einem EIGENEN Prozess, weil sich ortools und
|
||||
highspy auf vielen Systemen nicht gemeinsam importieren lassen (beide bringen
|
||||
eine eigene HiGHS-Kopie mit -> Symbolkonflikt).
|
||||
|
||||
Die Isolation besorgt ein ProcessPoolExecutor. Drei Einstellungen ergeben
|
||||
zusammen die Garantie:
|
||||
|
||||
mp_context "spawn" Der Kindprozess startet mit einem FRISCHEN
|
||||
Interpreter, statt den Speicher des Elternprozesses
|
||||
zu erben. Was hier schon importiert ist, ist dort
|
||||
nicht importiert. Mit dem Standard "fork" auf Linux
|
||||
waere das nicht so.
|
||||
max_tasks_per_child=1 Jede Aufgabe bekommt einen NEUEN Prozess. Ohne das
|
||||
wuerde der Pool seinen Arbeiter wiederverwenden - und
|
||||
beim zweiten Solver waere der Konflikt zurueck.
|
||||
max_workers=1 Haelt die vier Laeufe nacheinander. Nicht aus
|
||||
Vorsicht, sondern damit die gemessenen Zeiten
|
||||
vergleichbar bleiben.
|
||||
|
||||
Jeder Solver steht in einer eigenen Funktion mit LOKALEM Import. Das ist der
|
||||
Unterschied zu einem Codestring, den man an 'python -c' uebergibt: Die
|
||||
Funktion laesst sich einzeln aufrufen, testen und vom Editor pruefen - ein
|
||||
String nicht.
|
||||
|
||||
Benoetigt: scipy, highspy, cvxpy, ortools
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import multiprocessing
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
ERWARTET = 530.0 # Ergebnis der Handrechnung zum Produktionsprogramm
|
||||
|
||||
# Jeder Eintrag ist ein eigenständiges Miniprogramm, das sein Ergebnis als
|
||||
# JSON auf stdout ausgibt. So bleibt jeder Import in seinem eigenen Prozess.
|
||||
ANSAETZE: dict[str, str] = {
|
||||
# Die Instanz - einmal notiert, von allen vier Funktionen benutzt.
|
||||
ZIEL = [10.0, 15.0, 25.0]
|
||||
MATRIX = [[1, 1, 2], [2, 3, 1]]
|
||||
KAPAZITAET = [40.0, 50.0]
|
||||
|
||||
"scipy.optimize.linprog": """
|
||||
|
||||
def loese_mit_scipy() -> tuple[float, list[float]]:
|
||||
from scipy.optimize import linprog
|
||||
res = linprog(c=[-10.0, -15.0, -25.0], # linprog MINIMIERT -> negieren
|
||||
A_ub=[[1, 1, 2], [2, 3, 1]], b_ub=[40, 50],
|
||||
ergebnis = linprog(c=[-w for w in ZIEL], # linprog MINIMIERT -> negieren
|
||||
A_ub=MATRIX, b_ub=KAPAZITAET,
|
||||
bounds=[(0, None)] * 3, method="highs")
|
||||
ausgabe = (-res.fun, list(res.x))
|
||||
""",
|
||||
return -ergebnis.fun, list(ergebnis.x)
|
||||
|
||||
"highspy (natives HiGHS)": """
|
||||
import numpy as np, highspy
|
||||
|
||||
def loese_mit_highspy() -> tuple[float, list[float]]:
|
||||
import highspy
|
||||
import numpy as np
|
||||
h = highspy.Highs()
|
||||
h.setOptionValue("output_flag", False)
|
||||
h.addVars(3, np.zeros(3), np.full(3, highspy.kHighsInf))
|
||||
h.changeObjectiveSense(highspy.ObjSense.kMaximize)
|
||||
for j, wert in enumerate([10.0, 15.0, 25.0]):
|
||||
for j, wert in enumerate(ZIEL):
|
||||
h.changeColCost(j, wert)
|
||||
# CSR-Format: starts[i] = Beginn von Zeile i in indices/values
|
||||
h.addRows(2, np.full(2, -highspy.kHighsInf), np.array([40.0, 50.0]), 6,
|
||||
h.addRows(2, np.full(2, -highspy.kHighsInf), np.array(KAPAZITAET), 6,
|
||||
np.array([0, 3], dtype=np.int32),
|
||||
np.array([0, 1, 2, 0, 1, 2], dtype=np.int32),
|
||||
np.array([1.0, 1.0, 2.0, 2.0, 3.0, 1.0]))
|
||||
np.array([float(w) for zeile in MATRIX for w in zeile]))
|
||||
h.run()
|
||||
ausgabe = (h.getInfo().objective_function_value,
|
||||
return (h.getInfo().objective_function_value,
|
||||
list(h.getSolution().col_value[:3]))
|
||||
""",
|
||||
|
||||
"cvxpy": """
|
||||
import numpy as np, cvxpy as cp
|
||||
|
||||
def loese_mit_cvxpy() -> tuple[float, list[float]]:
|
||||
import cvxpy as cp
|
||||
import numpy as np
|
||||
x = cp.Variable(3, nonneg=True)
|
||||
problem = cp.Problem(cp.Maximize(np.array([10.0, 15.0, 25.0]) @ x),
|
||||
[np.array([[1, 1, 2], [2, 3, 1]]) @ x <= np.array([40, 50])])
|
||||
problem = cp.Problem(cp.Maximize(np.array(ZIEL) @ x),
|
||||
[np.array(MATRIX) @ x <= np.array(KAPAZITAET)])
|
||||
problem.solve()
|
||||
ausgabe = (float(problem.value), [float(v) for v in x.value])
|
||||
""",
|
||||
return float(problem.value), [float(v) for v in x.value]
|
||||
|
||||
"ortools / GLOP": """
|
||||
|
||||
def loese_mit_ortools() -> tuple[float, list[float]]:
|
||||
from ortools.linear_solver import pywraplp
|
||||
s = pywraplp.Solver.CreateSolver("GLOP")
|
||||
x = [s.NumVar(0, s.infinity(), f"x{j+1}") for j in range(3)]
|
||||
A = [[1, 1, 2], [2, 3, 1]]
|
||||
for i, kap in enumerate([40, 50]):
|
||||
s.Add(sum(A[i][j] * x[j] for j in range(3)) <= kap)
|
||||
s.Maximize(10 * x[0] + 15 * x[1] + 25 * x[2])
|
||||
for i, kapazitaet in enumerate(KAPAZITAET):
|
||||
s.Add(sum(MATRIX[i][j] * x[j] for j in range(3)) <= kapazitaet)
|
||||
s.Maximize(sum(ZIEL[j] * x[j] for j in range(3)))
|
||||
s.Solve()
|
||||
ausgabe = (s.Objective().Value(), [v.solution_value() for v in x])
|
||||
""",
|
||||
return s.Objective().Value(), [v.solution_value() for v in x]
|
||||
|
||||
|
||||
ANSAETZE = {
|
||||
"scipy.optimize.linprog": loese_mit_scipy,
|
||||
"highspy (natives HiGHS)": loese_mit_highspy,
|
||||
"cvxpy": loese_mit_cvxpy,
|
||||
"ortools / GLOP": loese_mit_ortools,
|
||||
}
|
||||
|
||||
|
||||
def fuehre_in_eigenem_prozess_aus(quelltext: str) -> tuple[float, list[float]]:
|
||||
"""Startet den Codeschnipsel als separaten Python-Prozess und liest das Ergebnis."""
|
||||
programm = textwrap.dedent(quelltext) + "\nimport json; print(json.dumps(ausgabe))\n"
|
||||
ergebnis = subprocess.run([sys.executable, "-c", programm],
|
||||
capture_output=True, text=True, timeout=120)
|
||||
if ergebnis.returncode != 0:
|
||||
raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])
|
||||
wert, loesung = json.loads(ergebnis.stdout.strip().splitlines()[-1])
|
||||
return wert, loesung
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 78)
|
||||
print(" EIN SYSTEM - VIER ANSAETZE (je eigener Prozess)")
|
||||
|
|
@ -95,14 +114,20 @@ if __name__ == "__main__":
|
|||
print("-" * 78)
|
||||
|
||||
werte = []
|
||||
for name, quelltext in ANSAETZE.items():
|
||||
t0 = time.perf_counter()
|
||||
# Ein Pool, vier Aufgaben, vier frische Prozesse. Der Kontext muss
|
||||
# "spawn" sein - siehe Modulkommentar.
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
for name, funktion in ANSAETZE.items():
|
||||
beginn = time.perf_counter()
|
||||
try:
|
||||
wert, x = fuehre_in_eigenem_prozess_aus(quelltext)
|
||||
except RuntimeError as fehler:
|
||||
print(f"{name:<26} nicht verfuegbar: {fehler[:40]}")
|
||||
wert, x = pool.submit(funktion).result(timeout=120)
|
||||
except Exception as fehler: # Bibliothek fehlt o. Ae.
|
||||
print(f"{name:<26} nicht verfuegbar: {str(fehler)[:40]}")
|
||||
continue
|
||||
dauer = time.perf_counter() - t0
|
||||
dauer = time.perf_counter() - beginn
|
||||
werte.append(wert)
|
||||
print(f"{name:<26} {wert:>10.2f} {x[0]:>7.2f} {x[1]:>7.2f} {x[2]:>7.2f} "
|
||||
f"{dauer:>8.2f} s")
|
||||
|
|
@ -115,6 +140,6 @@ if __name__ == "__main__":
|
|||
assert spanne < 1e-6, "Die Bibliotheken widersprechen sich!"
|
||||
assert abs(werte[0] - ERWARTET) < 1e-6, "Ergebnis weicht von der Handrechnung ab!"
|
||||
print("Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.")
|
||||
print("(Die Zeiten enthalten den Prozessstart und den Import - sie messen")
|
||||
print(" NICHT die reine Solverleistung, siehe Uebung 3.5.)")
|
||||
print("(Die Zeiten enthalten Prozessstart und Import - sie messen NICHT die")
|
||||
print(" reine Solverleistung. Die Uebungsaufgabe 'Laufzeitvergleich' trennt beides.)")
|
||||
print("=" * 78)
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ Benoetigt: numpy, pydantic, ortools, highspy (jeweils im eigenen Prozess)
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import multiprocessing
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
import numpy as np
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
|
@ -241,25 +241,29 @@ def geoeffnete_lager(problem: Standortproblem, loesung: Loesung) -> list[str]:
|
|||
if any(loesung.werte[problem.schluessel(i, j)] > 0.5 for j in range(m))]
|
||||
|
||||
|
||||
def loese_in_eigenem_prozess(name: str) -> Loesung:
|
||||
"""Startet dieses Programm noch einmal - mit genau einem Solverimport."""
|
||||
ergebnis = subprocess.run([sys.executable, __file__, name],
|
||||
capture_output=True, text=True, timeout=300)
|
||||
if ergebnis.returncode != 0:
|
||||
raise RuntimeError(ergebnis.stderr.strip().splitlines()[-1])
|
||||
# Das DTO als JSON - genau dafuer ist ein Datenobjekt ohne Solverbezug gut.
|
||||
return Loesung.model_validate_json(ergebnis.stdout.strip().splitlines()[-1])
|
||||
def loese_in_eigenem_prozess(name: str, problem: Standortproblem) -> Loesung:
|
||||
"""Laesst genau einen Modellbauer in einem frischen Prozess rechnen.
|
||||
|
||||
'spawn' statt des Linux-Standards 'fork': Der Kindprozess startet mit
|
||||
einem leeren Interpreter und importiert nur den Solver, den SEIN
|
||||
Modellbauer braucht. max_tasks_per_child=1 sorgt dafuer, dass der Pool
|
||||
seinen Arbeiter nicht wiederverwendet - sonst saessen beim zweiten Aufruf
|
||||
wieder beide Bibliotheken im selben Prozess.
|
||||
|
||||
Hin und zurueck wandert das Domaenenmodell bzw. das Loesungs-DTO. Beide
|
||||
kennen keinen Solver, sind also serialisierbar - genau dafuer sind sie da.
|
||||
"""
|
||||
with ProcessPoolExecutor(
|
||||
max_workers=1,
|
||||
mp_context=multiprocessing.get_context("spawn"),
|
||||
max_tasks_per_child=1) as pool:
|
||||
return pool.submit(MODELLBAUER[name], problem).result(timeout=300)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
problem = beispielproblem()
|
||||
|
||||
# --- Kindprozess: rechnen und das DTO als JSON ausgeben ---------------
|
||||
if len(sys.argv) > 1:
|
||||
print(MODELLBAUER[sys.argv[1]](problem).model_dump_json())
|
||||
sys.exit(0)
|
||||
|
||||
# --- Hauptprozess: beide Solver anstossen und vergleichen -------------
|
||||
# --- Beide Solver anstossen und vergleichen ---------------------------
|
||||
print("=" * 82)
|
||||
print(" DERSELBE FALL, ZWEI SOLVER - UND EIN AUSWERTUNGSCODE")
|
||||
print("=" * 82)
|
||||
|
|
@ -271,7 +275,7 @@ if __name__ == "__main__":
|
|||
loesungen: dict[str, Loesung] = {}
|
||||
for name, beschriftung in [("cpsat", "OR-Tools CP-SAT"),
|
||||
("highs", "HiGHS (highspy)")]:
|
||||
loesung = loesungen[name] = loese_in_eigenem_prozess(name)
|
||||
loesung = loesungen[name] = loese_in_eigenem_prozess(name, problem)
|
||||
beanstandungen = pruefe_zuordnung(problem, loesung)
|
||||
|
||||
print(f"{beschriftung}")
|
||||
|
|
|
|||
62
PROGRESS.md
62
PROGRESS.md
|
|
@ -77,14 +77,14 @@ sie unerwartet ab, ist etwas kaputtgegangen.
|
|||
| Kapiteldateien | 22 | **36** (31 + 5 Teil-Synthesen) |
|
||||
| Kapitel | 15 | **23** |
|
||||
| Anhänge | 4 | **5** |
|
||||
| Zeilen im Gesamtdokument | 11 082 | **28 510** |
|
||||
| Größe des Gesamtdokuments | 606 KB | **1 618 KB** |
|
||||
| Hauptüberschriften | 131 | **303** |
|
||||
| Zeilen im Gesamtdokument | 11 082 | **28 565** |
|
||||
| Größe des Gesamtdokuments | 606 KB | **1 622 KB** |
|
||||
| Hauptüberschriften | 131 | **302** |
|
||||
| registrierte Abschnitte | 122 | **296** |
|
||||
| aufgelöste Querverweise | 314 | **815** (0 unaufgelöst) |
|
||||
| aufgelöste Querverweise | 314 | **818** (0 unaufgelöst) |
|
||||
| Indexmarken | 295 | **328** |
|
||||
| Beispielprogramme | 41 | **76** (alle lauffähig) |
|
||||
| PDF-Seiten | — | **758** |
|
||||
| PDF-Seiten | — | **760** |
|
||||
| Notebooks | — | **25** |
|
||||
| Plotly-Figuren | — | **4** |
|
||||
| Diagramme (SVG) | 26 | **33**, davon **19** mit Generatorskript (16 Skripte) |
|
||||
|
|
@ -1770,6 +1770,58 @@ Gegengetestet mit beiden Bruchformen.
|
|||
Stand danach: **36 Dateien** (31 + 5 Synthesen), 296 Abschnitte, **815** Querverweise,
|
||||
303 Hauptüberschriften, PDF **758** Seiten.
|
||||
|
||||
### ✅ 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.
|
||||
|
||||
**Was ersetzt wurde.** `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 für
|
||||
die Instanzgröße. Aus jeder Variante ist jetzt eine gewöhnliche Funktion mit **lokalem
|
||||
Import** geworden. `Solverwechsel_CPSAT_HiGHS.py` rief sich selbst über `sys.argv` erneut
|
||||
auf; auch das entfällt.
|
||||
|
||||
Ausgeführt wird über einen `ProcessPoolExecutor` mit zwei Einstellungen, die zusammen die
|
||||
Garantie ergeben — und beide sind nötig:
|
||||
|
||||
* `mp_context="spawn"` — frischer Interpreter statt geerbtem Speicher. Unter Linux ist
|
||||
`fork` der Standard, und damit wäre alles bereits Importierte auch im Kind importiert.
|
||||
* `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 zurück. Nachgemessen:
|
||||
vier Aufgaben, vier verschiedene PIDs.
|
||||
|
||||
Der zweite Punkt hat einen eigenen ⚠️-Kasten bekommen, weil der Fehler leicht zu machen und
|
||||
schwer zu finden ist: Der Absturz käme nicht beim ersten Solver, sondern beim zweiten — und
|
||||
sähe aus wie ein Problem des zweiten.
|
||||
|
||||
**Regel 4, dreifach geprüft.** Alle drei Programme drucken Ausgaben, die im Buch stehen:
|
||||
|
||||
* `Ein_System_Vier_Ansaetze.py`: identisch bis auf die Zeitspalte, **einschließlich der
|
||||
Spannweite 2,41 · 10⁻⁸**, auf die sich der Merksatz des Kapitels beruft.
|
||||
* `Benchmark_Skalierung.py`: **alle zwölf Zielwerte und alle drei Spannweiten
|
||||
bitgleich**; Zeiten und Speicher haben sich verschoben, beide sind im Abdruck seit jeher
|
||||
als hardwareabhängig gekennzeichnet.
|
||||
* `Solverwechsel_CPSAT_HiGHS.py`: Ausgabe ohne Zeiten unverändert.
|
||||
|
||||
**Was bewusst `subprocess` bleibt:** `Mutationstest.py`. Dort wird pytest auf einer
|
||||
**mutierten Kopie** in einem temporären Verzeichnis gestartet — ein externes Werkzeug auf
|
||||
veränderten Dateien, nicht die Isolation eines Imports. Für diesen Fall ist `subprocess`
|
||||
richtig.
|
||||
|
||||
**Neu im Kapitel Ökosystem:** ein Abschnitt „Wie die Isolation aussieht, wenn sie tragen
|
||||
soll" — warum ein Codestring die schlechteste Umsetzung von „eigener Prozess" ist (unsichtbar
|
||||
für Editor, Linter und Testwerkzeug; Tippfehler fallen erst zur Laufzeit auf; übergeben
|
||||
lassen sich nur Zeichenketten). Anhang C nennt jetzt ebenfalls `ProcessPoolExecutor` statt
|
||||
`subprocess`.
|
||||
|
||||
**Ein eigener Fehler, gefunden und abgesichert:** Ich hatte dem neuen `###` ein
|
||||
`{#sec:...}`-Label gegeben. `ABSCHNITT_RE` erkennt nur `## ` — das Label wäre nie
|
||||
registriert worden, und jeder Verweis darauf ins Leere gelaufen, ohne Warnung. Label
|
||||
entfernt, und `--check` meldet diesen Fall jetzt. Gegengetestet.
|
||||
|
||||
Stand danach: 818 Querverweise, PDF **760** Seiten, 69 netzfreie Programme fehlerfrei.
|
||||
|
||||
---
|
||||
|
||||
## 8. Commit-Historie des V04-Strangs
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ daraus ab, und alle Befehle unten werden **hier** ausgeführt.
|
|||
| `Operations_Research_mit_Python_Version_04/` | **Quelle**: 36 Kapiteldateien (inkl. 5 Teil-Synthesen) + Build-Skripte |
|
||||
| `bilder_04/` | **Quelle**: Diagramme (SVG/PNG) + `erzeuge_*.py`-Generatoren |
|
||||
| `Operations_Research_mit_Python_Version_04.md` | generiert: Gesamtdokument (Pandoc-Eingabe) |
|
||||
| `Operations_Research_mit_Python_Version_04.pdf` | generiert: PDF, 758 Seiten |
|
||||
| `Operations_Research_mit_Python_Version_04.pdf` | generiert: PDF, 760 Seiten |
|
||||
| `OR_HTML_04/` | generiert: **Mehrseiten-Website** — dieser Ordner wird veröffentlicht |
|
||||
| `Operations_Research_mit_Python_Version_04_Programme/` | generiert: 76 lauffähige Beispielprogramme |
|
||||
| `Notebooks_04/` | generiert: ein Jupyter-Notebook je Kapitel |
|
||||
|
|
|
|||
Loading…
Reference in a new issue