|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "metadata": {}, |
| 6 | + "source": [ |
| 7 | + "# Brute Force: 100 Dimensions (Ackley Function)\n", |
| 8 | + "\n", |
| 9 | + "This notebook demonstrates how to use the `Spot` class from `spotpython` with and without the Nyström approximation for Kriging surrogates on the 100-dimensional Ackley function.\n", |
| 10 | + "\n", |
| 11 | + "We use a maximum of 500 function evaluations." |
| 12 | + ] |
| 13 | + }, |
| 14 | + { |
| 15 | + "cell_type": "code", |
| 16 | + "execution_count": 1, |
| 17 | + "metadata": {}, |
| 18 | + "outputs": [ |
| 19 | + { |
| 20 | + "name": "stdout", |
| 21 | + "output_type": "stream", |
| 22 | + "text": [ |
| 23 | + "--- Expertenbericht: Multi-Start-Optimierung (100D Ackley) ---\n", |
| 24 | + "Dimension (D): 100\n", |
| 25 | + "Gesamtbudget (FEs): 500\n", |
| 26 | + "Lokaler Suchalgorithmus: 'Powell' (ableitungsfrei)\n", |
| 27 | + "---\n", |
| 28 | + "Strategische Budget-Allokation:\n", |
| 29 | + " Anzahl Starts (n): 25\n", |
| 30 | + " Budget pro Start (b): 20 FEs (lokale Suche)\n", |
| 31 | + " Erwartete Gesamt-FEs: 500\n", |
| 32 | + "---\n", |
| 33 | + "Starte Optimierungsläufe...\n", |
| 34 | + " Lauf 1/25 abgeschlossen. End-Wert: 21.1100 (Lokale FEs: 20/20)\n", |
| 35 | + " Lauf 2/25 abgeschlossen. End-Wert: 21.3363 (Lokale FEs: 20/20)\n", |
| 36 | + " Lauf 3/25 abgeschlossen. End-Wert: 21.3037 (Lokale FEs: 20/20)\n", |
| 37 | + " Lauf 4/25 abgeschlossen. End-Wert: 21.1079 (Lokale FEs: 20/20)\n", |
| 38 | + " Lauf 5/25 abgeschlossen. End-Wert: 21.1091 (Lokale FEs: 20/20)\n", |
| 39 | + " Lauf 6/25 abgeschlossen. End-Wert: 21.2609 (Lokale FEs: 20/20)\n", |
| 40 | + " Lauf 7/25 abgeschlossen. End-Wert: 21.2182 (Lokale FEs: 20/20)\n", |
| 41 | + " Lauf 8/25 abgeschlossen. End-Wert: 21.3431 (Lokale FEs: 20/20)\n", |
| 42 | + " Lauf 9/25 abgeschlossen. End-Wert: 21.2842 (Lokale FEs: 20/20)\n", |
| 43 | + " Lauf 10/25 abgeschlossen. End-Wert: 21.3092 (Lokale FEs: 20/20)\n", |
| 44 | + " Lauf 11/25 abgeschlossen. End-Wert: 21.1805 (Lokale FEs: 20/20)\n", |
| 45 | + " Lauf 12/25 abgeschlossen. End-Wert: 21.2944 (Lokale FEs: 20/20)\n", |
| 46 | + " Lauf 13/25 abgeschlossen. End-Wert: 21.2102 (Lokale FEs: 20/20)\n", |
| 47 | + " Lauf 14/25 abgeschlossen. End-Wert: 21.2913 (Lokale FEs: 20/20)\n", |
| 48 | + " Lauf 15/25 abgeschlossen. End-Wert: 21.3353 (Lokale FEs: 20/20)\n", |
| 49 | + " Lauf 16/25 abgeschlossen. End-Wert: 21.1844 (Lokale FEs: 20/20)\n", |
| 50 | + " Lauf 17/25 abgeschlossen. End-Wert: 21.2403 (Lokale FEs: 20/20)\n", |
| 51 | + " Lauf 18/25 abgeschlossen. End-Wert: 21.1968 (Lokale FEs: 20/20)\n", |
| 52 | + " Lauf 19/25 abgeschlossen. End-Wert: 21.0778 (Lokale FEs: 20/20)\n", |
| 53 | + " Lauf 20/25 abgeschlossen. End-Wert: 21.2517 (Lokale FEs: 20/20)\n", |
| 54 | + " Lauf 21/25 abgeschlossen. End-Wert: 21.2232 (Lokale FEs: 20/20)\n", |
| 55 | + " Lauf 22/25 abgeschlossen. End-Wert: 21.2731 (Lokale FEs: 20/20)\n", |
| 56 | + " Lauf 23/25 abgeschlossen. End-Wert: 21.4001 (Lokale FEs: 20/20)\n", |
| 57 | + " Lauf 24/25 abgeschlossen. End-Wert: 21.2894 (Lokale FEs: 20/20)\n", |
| 58 | + " Lauf 25/25 abgeschlossen. End-Wert: 21.2248 (Lokale FEs: 20/20)\n", |
| 59 | + "---\n", |
| 60 | + "Alle Optimierungsläufe abgeschlossen.\n", |
| 61 | + "Gesamtlaufzeit: 0.01 Sekunden\n", |
| 62 | + "Verbrauchtes Gesamtbudget: 500 / 500 FEs\n", |
| 63 | + "---\n", |
| 64 | + "\n", |
| 65 | + "--- Zusammenfassung der Ergebnisse ---\n", |
| 66 | + "Tabelle 1: Ergebnisse der Multi-Start-Optimierungsläufe\n", |
| 67 | + "======================================================================\n", |
| 68 | + "Lauf-ID | f(x_end) | Lokale FEs | Status\n", |
| 69 | + "----------------------------------------------------------------------\n", |
| 70 | + "0 | 21.1100 | 20 | Max FEs erreicht\n", |
| 71 | + "1 | 21.3363 | 20 | Max FEs erreicht\n", |
| 72 | + "2 | 21.3037 | 20 | Max FEs erreicht\n", |
| 73 | + "3 | 21.1079 | 20 | Max FEs erreicht\n", |
| 74 | + "4 | 21.1091 | 20 | Max FEs erreicht\n", |
| 75 | + "5 | 21.2609 | 20 | Max FEs erreicht\n", |
| 76 | + "6 | 21.2182 | 20 | Max FEs erreicht\n", |
| 77 | + "7 | 21.3431 | 20 | Max FEs erreicht\n", |
| 78 | + "8 | 21.2842 | 20 | Max FEs erreicht\n", |
| 79 | + "9 | 21.3092 | 20 | Max FEs erreicht\n", |
| 80 | + "10 | 21.1805 | 20 | Max FEs erreicht\n", |
| 81 | + "11 | 21.2944 | 20 | Max FEs erreicht\n", |
| 82 | + "12 | 21.2102 | 20 | Max FEs erreicht\n", |
| 83 | + "13 | 21.2913 | 20 | Max FEs erreicht\n", |
| 84 | + "14 | 21.3353 | 20 | Max FEs erreicht\n", |
| 85 | + "15 | 21.1844 | 20 | Max FEs erreicht\n", |
| 86 | + "16 | 21.2403 | 20 | Max FEs erreicht\n", |
| 87 | + "17 | 21.1968 | 20 | Max FEs erreicht\n", |
| 88 | + "18 | 21.0778 | 20 | Max FEs erreicht\n", |
| 89 | + "19 | 21.2517 | 20 | Max FEs erreicht\n", |
| 90 | + "20 | 21.2232 | 20 | Max FEs erreicht\n", |
| 91 | + "21 | 21.2731 | 20 | Max FEs erreicht\n", |
| 92 | + "22 | 21.4001 | 20 | Max FEs erreicht\n", |
| 93 | + "23 | 21.2894 | 20 | Max FEs erreicht\n", |
| 94 | + "24 | 21.2248 | 20 | Max FEs erreicht\n", |
| 95 | + "======================================================================\n", |
| 96 | + "\n", |
| 97 | + "--- Bestes gefundenes Minimum ---\n", |
| 98 | + "Bester Lauf (ID): 18\n", |
| 99 | + "Bester Funktionswert: 21.07782929\n", |
| 100 | + " (Globales Optimum ist: 0.0)\n", |
| 101 | + " Bester Vektor (x*): [-0.00, -0.00, -23.92, 12.68, 11.45, ...]\n" |
| 102 | + ] |
| 103 | + } |
| 104 | + ], |
| 105 | + "source": [ |
| 106 | + "import numpy as np\n", |
| 107 | + "from scipy.optimize import minimize\n", |
| 108 | + "import time\n", |
| 109 | + "import warnings\n", |
| 110 | + "\n", |
| 111 | + "# ---\n", |
| 112 | + "# 1. Definition der Zielfunktion: 100D Ackley\n", |
| 113 | + "# ---\n", |
| 114 | + "# Basierend auf der Standarddefinition [1, 2, 3]\n", |
| 115 | + "# Globales Optimum: f(x*) = 0 bei x* = [0,..., 0]\n", |
| 116 | + "# ---\n", |
| 117 | + "\n", |
| 118 | + "def ackley(x):\n", |
| 119 | + " \"\"\"\n", |
| 120 | + " Berechnet die n-dimensionale Ackley-Funktion.\n", |
| 121 | + " Parameter a=20, b=0.2, c=2*pi werden standardmäßig verwendet.\n", |
| 122 | + " \"\"\"\n", |
| 123 | + " a = 20.0\n", |
| 124 | + " b = 0.2\n", |
| 125 | + " c = 2.0 * np.pi\n", |
| 126 | + " \n", |
| 127 | + " # Dimension aus dem Input-Vektor ableiten\n", |
| 128 | + " d = len(x) \n", |
| 129 | + " \n", |
| 130 | + " # Berechne die beiden Terme der Funktion\n", |
| 131 | + " sum_sq_term = -b * np.sqrt(np.sum(x**2) / d)\n", |
| 132 | + " cos_term = np.sum(np.cos(c * x)) / d\n", |
| 133 | + " \n", |
| 134 | + " # Kombiniere die Terme\n", |
| 135 | + " result = -a * np.exp(sum_sq_term) - np.exp(cos_term) + a + np.e\n", |
| 136 | + " \n", |
| 137 | + " return result\n", |
| 138 | + "\n", |
| 139 | + "# ---\n", |
| 140 | + "# 2. Definition der globalen Parameter und der Budget-Allokation\n", |
| 141 | + "# ---\n", |
| 142 | + "\n", |
| 143 | + "# Problem-Dimension\n", |
| 144 | + "DIMENSIONS = 100\n", |
| 145 | + "\n", |
| 146 | + "# Suchraum-Grenzen \n", |
| 147 | + "BOUND_LOW = -32.768\n", |
| 148 | + "BOUND_HIGH = 32.768\n", |
| 149 | + "\n", |
| 150 | + "# Globales Budget für Funktionsauswertungen\n", |
| 151 | + "BUDGET_TOTAL = 500\n", |
| 152 | + "\n", |
| 153 | + "# ---\n", |
| 154 | + "# 3. Strategische Bestimmung von n (Anzahl Starts) und b (Budget pro Lauf)\n", |
| 155 | + "# ---\n", |
| 156 | + "# Wie in Abschnitt 2 des Berichts hergeleitet:\n", |
| 157 | + "# Wir wählen n=10, um eine minimale Exploration zu gewährleisten.\n", |
| 158 | + "# Dies lässt b=50 FEs pro lokaler Suche (inkl. Start-FE).\n", |
| 159 | + "# n * b = 10 * 50 = 500.\n", |
| 160 | + "# ---\n", |
| 161 | + "\n", |
| 162 | + "N_STARTS = 25\n", |
| 163 | + "BUDGET_PER_RUN = BUDGET_TOTAL // N_STARTS # Ergibt 50\n", |
| 164 | + "\n", |
| 165 | + "# ---\n", |
| 166 | + "# 4. Haupt-Optimierungsfunktion\n", |
| 167 | + "# ---\n", |
| 168 | + "\n", |
| 169 | + "def run_multi_start_optimization():\n", |
| 170 | + " \"\"\"\n", |
| 171 | + " Führt die Multi-Start-Optimierung mit dem Powell-Verfahren durch.\n", |
| 172 | + " \"\"\"\n", |
| 173 | + " print(\"--- Expertenbericht: Multi-Start-Optimierung (100D Ackley) ---\")\n", |
| 174 | + " print(f\"Dimension (D): {DIMENSIONS}\")\n", |
| 175 | + " print(f\"Gesamtbudget (FEs): {BUDGET_TOTAL}\")\n", |
| 176 | + " print(f\"Lokaler Suchalgorithmus: 'Powell' (ableitungsfrei)\")\n", |
| 177 | + " print(\"---\")\n", |
| 178 | + " print(f\"Strategische Budget-Allokation:\")\n", |
| 179 | + " print(f\" Anzahl Starts (n): {N_STARTS}\")\n", |
| 180 | + " print(f\" Budget pro Start (b): {BUDGET_PER_RUN} FEs (lokale Suche)\")\n", |
| 181 | + " print(f\" Erwartete Gesamt-FEs: {N_STARTS * BUDGET_PER_RUN}\")\n", |
| 182 | + " print(\"---\")\n", |
| 183 | + " \n", |
| 184 | + " start_time = time.time()\n", |
| 185 | + " \n", |
| 186 | + " # Für Reproduzierbarkeit\n", |
| 187 | + " rng = np.random.default_rng(seed=42)\n", |
| 188 | + " \n", |
| 189 | + " # Generiere n Startpunkte [13, 14]\n", |
| 190 | + " # Shape: (N_STARTS, DIMENSIONS)\n", |
| 191 | + " start_points = rng.uniform(BOUND_LOW, BOUND_HIGH, size=(N_STARTS, DIMENSIONS))\n", |
| 192 | + " \n", |
| 193 | + " # Definiere die Bounds für scipy.optimize.minimize [4, 10]\n", |
| 194 | + " # Powell unterstützt 'bounds'\n", |
| 195 | + " # bounds = * DIMENSIONS\n", |
| 196 | + " bounds = [(BOUND_LOW, BOUND_HIGH)] * DIMENSIONS\n", |
| 197 | + " \n", |
| 198 | + " # Optionen für den Powell-Minimierer \n", |
| 199 | + " # Wir setzen maxfev auf unser berechnetes lokales Budget b.\n", |
| 200 | + " powell_options = {\n", |
| 201 | + " 'maxfev': BUDGET_PER_RUN,\n", |
| 202 | + " 'disp': False # Ausgabe pro Lauf unterdrücken\n", |
| 203 | + " }\n", |
| 204 | + " \n", |
| 205 | + " # Speichern der Ergebnisse\n", |
| 206 | + " all_results = []\n", |
| 207 | + " total_fevals_used = 0\n", |
| 208 | + " \n", |
| 209 | + " print(\"Starte Optimierungsläufe...\")\n", |
| 210 | + " \n", |
| 211 | + " # Unterdrücke Warnungen von SciPy, falls maxfev erreicht wird\n", |
| 212 | + " warnings.filterwarnings(\"ignore\", message=\".*Maximum number of function evaluations.*\")\n", |
| 213 | + " \n", |
| 214 | + " # --- Die Hauptschleife ---\n", |
| 215 | + " for i in range(N_STARTS):\n", |
| 216 | + " x0 = start_points[i]\n", |
| 217 | + " \n", |
| 218 | + " # Starte die lokale Suche\n", |
| 219 | + " # Die erste Auswertung (f(x0)) wird von minimize durchgeführt\n", |
| 220 | + " # und zählt zum Budget 'maxfev'.\n", |
| 221 | + " result = minimize(\n", |
| 222 | + " fun=ackley,\n", |
| 223 | + " x0=x0,\n", |
| 224 | + " method='Powell',\n", |
| 225 | + " bounds=bounds,\n", |
| 226 | + " options=powell_options\n", |
| 227 | + " )\n", |
| 228 | + " \n", |
| 229 | + " # Speichere relevante Daten\n", |
| 230 | + " all_results.append({\n", |
| 231 | + " 'run_id': i,\n", |
| 232 | + " 'f_start': ackley(x0) if result.nfev == 0 else np.nan, # Fallback\n", |
| 233 | + " 'f_end': result.fun,\n", |
| 234 | + " 'nfev_local': result.nfev, # FEs, die minimize genutzt hat\n", |
| 235 | + " 'success': result.success,\n", |
| 236 | + " 'message': result.message,\n", |
| 237 | + " 'best_x': result.x\n", |
| 238 | + " })\n", |
| 239 | + " \n", |
| 240 | + " total_fevals_used += result.nfev\n", |
| 241 | + " \n", |
| 242 | + " # Der Startwert f(x0) ist die erste Auswertung.\n", |
| 243 | + " # Wir müssen ihn separat holen, falls minimize ihn nicht liefert (sollte es aber)\n", |
| 244 | + " # Für das Reporting holen wir den Startwert:\n", |
| 245 | + " f_start_val = all_results[i]['f_start']\n", |
| 246 | + " if np.isnan(f_start_val) and result.nfev > 0:\n", |
| 247 | + " # Normalerweise nicht nötig, aber zur Sicherheit\n", |
| 248 | + " f_start_val = ackley(x0)\n", |
| 249 | + " # Diese FE ist bereits in result.nfev enthalten\n", |
| 250 | + " \n", |
| 251 | + " print(f\" Lauf {i+1}/{N_STARTS} abgeschlossen. \"\n", |
| 252 | + " f\"End-Wert: {result.fun:.4f} \"\n", |
| 253 | + " f\"(Lokale FEs: {result.nfev}/{BUDGET_PER_RUN})\")\n", |
| 254 | + " \n", |
| 255 | + " end_time = time.time()\n", |
| 256 | + " \n", |
| 257 | + " # ---\n", |
| 258 | + " # 5. Ergebnisanalyse und Zusammenfassung\n", |
| 259 | + " # ---\n", |
| 260 | + " print(\"---\")\n", |
| 261 | + " print(\"Alle Optimierungsläufe abgeschlossen.\")\n", |
| 262 | + " print(f\"Gesamtlaufzeit: {end_time - start_time:.2f} Sekunden\")\n", |
| 263 | + " print(f\"Verbrauchtes Gesamtbudget: {total_fevals_used} / {BUDGET_TOTAL} FEs\")\n", |
| 264 | + " print(\"---\")\n", |
| 265 | + " \n", |
| 266 | + " # Finde das beste Ergebnis über alle Läufe\n", |
| 267 | + " best_run = min(all_results, key=lambda r: r['f_end'])\n", |
| 268 | + " \n", |
| 269 | + " print(\"\\n--- Zusammenfassung der Ergebnisse ---\")\n", |
| 270 | + " \n", |
| 271 | + " # Erzeuge die Ergebnistabelle\n", |
| 272 | + " print(\"Tabelle 1: Ergebnisse der Multi-Start-Optimierungsläufe\")\n", |
| 273 | + " print(\"=\" * 70)\n", |
| 274 | + " print(f\"{'Lauf-ID':<8} | {'f(x_end)':<12} | {'Lokale FEs':<10} | {'Status'}\")\n", |
| 275 | + " print(\"-\" * 70)\n", |
| 276 | + " for res in all_results:\n", |
| 277 | + " # Status-Nachricht kürzen\n", |
| 278 | + " status = \"Max FEs erreicht\" if \"function evaluations\" in res['message'] else \"Andere\"\n", |
| 279 | + " if res['success']: status = \"Konvergiert\"\n", |
| 280 | + " \n", |
| 281 | + " print(f\"{res['run_id']:<8} | {res['f_end']:<12.4f} | {res['nfev_local']:<10} | {status}\")\n", |
| 282 | + " print(\"=\" * 70)\n", |
| 283 | + " \n", |
| 284 | + " print(\"\\n--- Bestes gefundenes Minimum ---\")\n", |
| 285 | + " print(f\"Bester Lauf (ID): {best_run['run_id']}\")\n", |
| 286 | + " print(f\"Bester Funktionswert: {best_run['f_end']:.8f}\")\n", |
| 287 | + " print(f\" (Globales Optimum ist: 0.0)\")\n", |
| 288 | + " # Zeige die ersten 5 von 100 Dimensionen des besten Vektors\n", |
| 289 | + " x_best = best_run['best_x']\n", |
| 290 | + " print(f\" Bester Vektor (x*): [{x_best[0]:.2f}, {x_best[1]:.2f}, {x_best[2]:.2f}, {x_best[3]:.2f}, {x_best[4]:.2f}, ...]\")\n", |
| 291 | + " \n", |
| 292 | + " return best_run\n", |
| 293 | + "\n", |
| 294 | + "# ---\n", |
| 295 | + "# 6. Skriptausführung\n", |
| 296 | + "# ---\n", |
| 297 | + "if __name__ == \"__main__\":\n", |
| 298 | + " best_result = run_multi_start_optimization()" |
| 299 | + ] |
| 300 | + } |
| 301 | + ], |
| 302 | + "metadata": { |
| 303 | + "kernelspec": { |
| 304 | + "display_name": "spot313", |
| 305 | + "language": "python", |
| 306 | + "name": "python3" |
| 307 | + }, |
| 308 | + "language_info": { |
| 309 | + "codemirror_mode": { |
| 310 | + "name": "ipython", |
| 311 | + "version": 3 |
| 312 | + }, |
| 313 | + "file_extension": ".py", |
| 314 | + "mimetype": "text/x-python", |
| 315 | + "name": "python", |
| 316 | + "nbconvert_exporter": "python", |
| 317 | + "pygments_lexer": "ipython3", |
| 318 | + "version": "3.13.7" |
| 319 | + } |
| 320 | + }, |
| 321 | + "nbformat": 4, |
| 322 | + "nbformat_minor": 2 |
| 323 | +} |
0 commit comments