Skip to content

Commit 81b628c

Browse files
committed
Add current fitness overlay visualization
1 parent 50d24a5 commit 81b628c

1 file changed

Lines changed: 113 additions & 0 deletions

File tree

notebooks/experiment_analysis.ipynb

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@
2727
"FINAL_TRAINING_DIR = REPO_ROOT / '.cache' / 'final_training'\n",
2828
"COLORS = {'RS': '#1f77b4', 'GA-STANDARD': '#ff7f0e', 'GA-MEMETIC': '#d62728', 'PSO': '#2ca02c'}\n",
2929
"\n",
30+
"\n",
31+
"def _exp_smooth(values: np.ndarray, smoothing_slider: float) -> np.ndarray:\n",
32+
" smoothed = np.empty_like(values, dtype=np.float32)\n",
33+
" last = values[0]\n",
34+
" for idx, point in enumerate(values):\n",
35+
" last = smoothing_slider * last + (1.0 - smoothing_slider) * point\n",
36+
" smoothed[idx] = last\n",
37+
" return smoothed\n",
38+
"\n",
39+
"\n",
3040
"def parse_experiment_name(exp_name):\n",
3141
" if '-' not in exp_name:\n",
3242
" return None, None\n",
@@ -258,6 +268,109 @@
258268
"plt.show()\n"
259269
]
260270
},
271+
{
272+
"cell_type": "markdown",
273+
"metadata": {},
274+
"source": [
275+
"## Current Fitness Overlays\n"
276+
]
277+
},
278+
{
279+
"cell_type": "code",
280+
"execution_count": null,
281+
"metadata": {},
282+
"outputs": [],
283+
"source": [
284+
"SMOOTHING_SLIDER_CURRENT_FITNESS = 0.5\n",
285+
"current_overlay = defaultdict(lambda: defaultdict(list))\n",
286+
"\n",
287+
"for exp_dir in sorted(EXPERIMENT_DIR.iterdir()):\n",
288+
" if not exp_dir.is_dir():\n",
289+
" continue\n",
290+
" model, optimizer = parse_experiment_name(exp_dir.name)\n",
291+
" if not optimizer:\n",
292+
" continue\n",
293+
" for run_dir in sorted(exp_dir.iterdir()):\n",
294+
" conv_path = run_dir / 'convergence.json'\n",
295+
" with open(conv_path) as f:\n",
296+
" conv = json.load(f)\n",
297+
" evaluations = conv.get('evaluations')\n",
298+
" current_fitness = conv.get('current_fitness')\n",
299+
" if evaluations and current_fitness:\n",
300+
" current_overlay[model][optimizer].append((evaluations, current_fitness))\n",
301+
"\n",
302+
"total_runs = sum(len(v) for model_data in current_overlay.values() for v in model_data.values())\n",
303+
"print(f\"Loaded current-fitness traces for {total_runs} runs\\n\")\n",
304+
"\n",
305+
"models = sorted(current_overlay.keys())\n",
306+
"# Prefer consistent optimizer ordering; fallback to sorted discovered optimizers\n",
307+
"preferred_order = [\"RS\", \"GA-STANDARD\", \"GA-MEMETIC\", \"PSO\"]\n",
308+
"observed_opts = sorted({opt for model_data in current_overlay.values() for opt in model_data.keys()})\n",
309+
"optimizer_order = [opt for opt in preferred_order if opt in observed_opts]\n",
310+
"optimizer_order += [opt for opt in observed_opts if opt not in optimizer_order]\n",
311+
"\n",
312+
"fig, axes = plt.subplots(len(optimizer_order), len(models), figsize=(6 * len(models), 3 * len(optimizer_order)))\n",
313+
"if len(optimizer_order) == 1 and len(models) == 1:\n",
314+
" axes = [[axes]]\n",
315+
"elif len(optimizer_order) == 1:\n",
316+
" axes = [axes]\n",
317+
"elif len(models) == 1:\n",
318+
" axes = [[ax] for ax in axes]\n",
319+
"\n",
320+
"for row_idx, optimizer in enumerate(optimizer_order):\n",
321+
" for col_idx, model in enumerate(models):\n",
322+
" ax = axes[row_idx][col_idx]\n",
323+
" runs = current_overlay[model].get(optimizer, [])\n",
324+
" if not runs:\n",
325+
" ax.text(0.5, 0.5, 'N/A', ha='center', va='center')\n",
326+
" ax.set_xticks([])\n",
327+
" ax.set_yticks([])\n",
328+
" if row_idx == 0:\n",
329+
" ax.set_title(model, fontweight='bold')\n",
330+
" if col_idx == 0:\n",
331+
" ax.set_ylabel(optimizer)\n",
332+
" continue\n",
333+
"\n",
334+
" base_eval = np.array(runs[0][0])\n",
335+
" aligned = []\n",
336+
" for evals, curr in runs:\n",
337+
" evals_arr = np.array(evals)\n",
338+
" curr_arr = np.array(curr)\n",
339+
" if len(evals_arr) != len(base_eval) or not np.allclose(evals_arr, base_eval):\n",
340+
" curr_arr = np.interp(base_eval, evals_arr, curr_arr)\n",
341+
" aligned.append(curr_arr)\n",
342+
" aligned = np.array(aligned)\n",
343+
" mean_curve = aligned.mean(axis=0)\n",
344+
" std_curve = aligned.std(axis=0)\n",
345+
" mean_smoothed = _exp_smooth(mean_curve, SMOOTHING_SLIDER_CURRENT_FITNESS)\n",
346+
" std_smoothed = _exp_smooth(std_curve, SMOOTHING_SLIDER_CURRENT_FITNESS)\n",
347+
" color = COLORS.get(optimizer, '#888888')\n",
348+
" ax.fill_between(base_eval, mean_smoothed - std_smoothed, mean_smoothed + std_smoothed,\n",
349+
" color=color, alpha=0.2)\n",
350+
" ax.plot(base_eval, mean_smoothed, label=optimizer, color=color, linewidth=2)\n",
351+
"\n",
352+
" # set per-model y-limits\n",
353+
" model_key = model.lower()\n",
354+
" if model_key == 'cnn':\n",
355+
" ax.set_ylim(0.40, 0.85)\n",
356+
" elif model_key == 'dt':\n",
357+
" ax.set_ylim(0.275, 0.36)\n",
358+
" elif model_key == 'knn':\n",
359+
" ax.set_ylim(0.225, 0.475)\n",
360+
"\n",
361+
" ax.grid(True, alpha=0.3)\n",
362+
" if row_idx == len(optimizer_order) - 1:\n",
363+
" ax.set_xlabel('Evaluation ($n$)')\n",
364+
" if col_idx == 0:\n",
365+
" ax.set_ylabel(f'{optimizer}\\nCurrent Fitness')\n",
366+
" if row_idx == 0:\n",
367+
" ax.set_title(f'{model}', fontweight='bold')\n",
368+
" ax.legend(fontsize=8, loc='best')\n",
369+
"\n",
370+
"plt.tight_layout()\n",
371+
"plt.show()\n"
372+
]
373+
},
261374
{
262375
"cell_type": "markdown",
263376
"metadata": {},

0 commit comments

Comments
 (0)