Analyzing Results
Objectives
Load and interpret optimization results
Compute performance metrics
Visualize solutions and convergence
Why This Matters
The Scenario: An R&D manager reviews the optimization results and sees “32 solutions found.” But the benchmark has 36 known optima. She asks: did we find everything, or are we missing solutions in hard-to-reach regions? She needs a clear metric and visualization to answer this — not just a count, but a measure of completeness across accuracy levels.
The Research Question: How do you quantify the quality of a multi-modal optimizer’s output — distinguishing “found most optima approximately” from “found all optima precisely” — and how do you visualize where solutions are and where gaps remain?
What This Episode Gives You: The analysis toolkit — Peak Ratio computation, convergence plots, 2D solution visualization, and a systematic approach to diagnosing when and why the optimizer misses optima.
Loading Results
If you saved results as shown in Episode 05:
import pandas as pd
import numpy as np
import json
# Load experiment results
exp_dir = "results/himmelblau_20260122_120000"
# Load solutions
solutions_df = pd.read_csv(f"{exp_dir}/solutions.csv")
solutions_np = np.load(f"{exp_dir}/solutions.npy")
# Load summary
with open(f"{exp_dir}/summary.json") as f:
summary = json.load(f)
print(f"Solutions found: {summary['n_solutions']}")
print(f"Function evaluations: {summary['n_function_evaluations']}")
Computing Metrics
Peak Ratio
from cec2013.cec2013 import CEC2013, how_many_goptima
# Load benchmark
f = CEC2013(4) # Himmelblau
n_optima = f.get_no_goptima()
# Extract solution coordinates (without function values)
found_solutions = solutions_np[:, :-1]
# Compute peak ratio at different accuracy levels
for acc in [1e-1, 1e-2, 1e-3, 1e-4, 1e-5]:
count, seeds = how_many_goptima(found_solutions, f, acc)
pr = count / n_optima
print(f"PR (ε={acc:.0e}): {pr:.2f} ({count}/{n_optima})")
Success Rate
For multiple runs:
def success_rate(peak_ratios, threshold=1.0):
"""Fraction of runs achieving PR >= threshold."""
return np.mean(np.array(peak_ratios) >= threshold)
# Example: 10 runs
peak_ratios = [1.0, 1.0, 0.75, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 0.75]
sr = success_rate(peak_ratios)
print(f"Success Rate: {sr:.1%}")
Visualization
2D Solution Plot
import matplotlib.pyplot as plt
import numpy as np
from cec2013.cec2013 import CEC2013, how_many_goptima
def plot_solutions_2d(func_id, found_solutions, title="Optimization Results"):
"""Plot found solutions on function contour."""
f = CEC2013(func_id)
dim = f.get_dimension()
n_optima = f.get_no_goptima()
# Get bounds
lb = [f.get_lbound(k) for k in range(dim)]
ub = [f.get_ubound(k) for k in range(dim)]
x = np.linspace(lb[0], ub[0], 100)
y = np.linspace(lb[1], ub[1], 100)
X, Y = np.meshgrid(x, y)
# Evaluate function on grid
Z = np.zeros_like(X)
for i in range(X.shape[0]):
for j in range(X.shape[1]):
Z[i, j] = f.evaluate(np.array([X[i, j], Y[i, j]]))
# Plot
fig, ax = plt.subplots(figsize=(10, 8))
contour = ax.contourf(X, Y, Z, levels=50, cmap='viridis', alpha=0.7)
plt.colorbar(contour, ax=ax, label='f(x)')
# Plot found solutions
ax.scatter(found_solutions[:, 0], found_solutions[:, 1],
c='red', s=100, marker='*', edgecolors='white',
label='Found Solutions', zorder=5)
# Mark global optima found
count, seeds = how_many_goptima(found_solutions, f, 0.0001)
if len(seeds) > 0:
ax.scatter(seeds[:, 0], seeds[:, 1],
c='lime', s=200, marker='o', facecolors='none',
linewidths=3, label='Global Optima', zorder=6)
ax.set_xlabel('x₁')
ax.set_ylabel('x₂')
ax.set_title(f"{title}\n{count}/{n_optima} global optima found")
ax.legend()
plt.tight_layout()
return fig
# Usage
fig = plot_solutions_2d(4, found_solutions, "Himmelblau - SHGA Results")
fig.savefig(f"{exp_dir}/solution_plot.png", dpi=150)
plt.show()
Convergence Plot
def plot_convergence(iteration_history, title="Convergence"):
"""Plot solutions found and evaluations over iterations."""
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
iters = iteration_history['iteration']
# Solutions over time
ax1.plot(iters, iteration_history['n_solutions'], 'b-o')
ax1.set_xlabel('Iteration')
ax1.set_ylabel('Solutions Found')
ax1.set_title('Solutions vs Iteration')
ax1.grid(True, alpha=0.3)
# Evaluations over time
ax2.plot(iters, iteration_history['n_evaluations'], 'r-o')
ax2.set_xlabel('Iteration')
ax2.set_ylabel('Function Evaluations')
ax2.set_title('Evaluations vs Iteration')
ax2.grid(True, alpha=0.3)
plt.suptitle(title)
plt.tight_layout()
return fig
Result Summary Table
For comparing multiple experiments:
from cec2013.cec2013 import CEC2013, how_many_goptima
def create_summary_table(experiments, func_id=4):
"""Create summary table from multiple experiments."""
f = CEC2013(func_id)
n_optima = f.get_no_goptima()
rows = []
for exp in experiments:
with open(f"{exp}/summary.json") as f_json:
summary = json.load(f_json)
solutions = np.load(f"{exp}/solutions.npy")
count, _ = how_many_goptima(solutions[:, :-1], f, 0.0001)
pr = count / n_optima
rows.append({
'Experiment': exp.split('/')[-1],
'Solutions': summary['n_solutions'],
'Evaluations': summary['n_function_evaluations'],
'Peak Ratio': f"{pr:.2f}"
})
return pd.DataFrame(rows)
# Example
table = create_summary_table([
"results/exp_001",
"results/exp_002",
"results/exp_003"
], func_id=4) # Himmelblau
print(table.to_markdown(index=False))
Interpretation Guide
Metric |
Good |
Excellent |
|---|---|---|
Peak Ratio (PR) |
> 0.7 |
= 1.0 |
Solutions Found |
≥ Known Optima |
= Known Optima |
Evaluations |
< Budget |
<< Budget |
When Results Are Poor
Issue |
Possible Cause |
Solution |
|---|---|---|
PR < 0.5 |
Insufficient budget |
Increase budget |
PR < 0.5 |
GA not diverse enough |
Increase population |
Many duplicates |
Solutions too close |
Increase clustering threshold |
Missing optima |
Small basin |
Decrease CMA-ES sigma |
SHGA performance across CEC2013 functions F4-F14. Left: Peak Ratio per function (green = perfect, orange = partial, red = poor). Right: found vs total optima count. The algorithm achieves perfect scores on low-dimensional functions and partial coverage on higher-dimensional compositions.
Keypoints
Peak Ratio (PR) is the primary metric: fraction of optima found
Compute PR at multiple accuracy levels (1e-1 to 1e-5)
Visualize 2D solutions with contour plots
Track convergence over iterations
PR = 1.0 means all known optima were found