211 lines
6.0 KiB
Python
211 lines
6.0 KiB
Python
#!/usr/bin/env python
|
|
"""
|
|
Generate paper figures from experimental results.
|
|
"""
|
|
|
|
import json
|
|
import matplotlib
|
|
|
|
matplotlib.use("Agg") # Non-interactive backend
|
|
import matplotlib.pyplot as plt
|
|
import numpy as np
|
|
|
|
|
|
def load_statistics():
|
|
"""Load statistics from JSON."""
|
|
with open("results/statistics.json", "r") as f:
|
|
return json.load(f)
|
|
|
|
|
|
def load_all_results():
|
|
"""Load all raw results."""
|
|
algorithms = ["gradient", "flooding", "random"]
|
|
all_data = {}
|
|
|
|
for algo in algorithms:
|
|
with open(f"results/{algo}/all_seeds.json", "r") as f:
|
|
all_data[algo] = json.load(f)
|
|
|
|
return all_data
|
|
|
|
|
|
def plot_pdr_vs_airtime(all_data, stats):
|
|
"""Generate PDR vs Airtime plot."""
|
|
fig, ax = plt.subplots(figsize=(8, 6))
|
|
|
|
colors = {"gradient": "#2E86AB", "flooding": "#E94F37", "random": "#7D8491"}
|
|
markers = {"gradient": "o", "flooding": "s", "random": "^"}
|
|
|
|
for algo in ["gradient", "flooding", "random"]:
|
|
algo_stats = stats[algo]
|
|
|
|
# Get mean and CI
|
|
pdr_mean = algo_stats["pdr"]["mean"]
|
|
pdr_ci = algo_stats["pdr"]["ci_95"]
|
|
air_mean = algo_stats["airtime_usage_percent"]["mean"]
|
|
air_ci = algo_stats["airtime_usage_percent"]["ci_95"]
|
|
|
|
# Plot with error bars
|
|
ax.errorbar(
|
|
air_mean,
|
|
pdr_mean,
|
|
xerr=air_ci,
|
|
yerr=pdr_ci,
|
|
fmt=markers[algo],
|
|
color=colors[algo],
|
|
markersize=12,
|
|
capsize=5,
|
|
capthick=2,
|
|
linewidth=2,
|
|
label=f"{algo.capitalize()} ({pdr_mean:.1f}%, {air_mean:.1f}%)",
|
|
)
|
|
|
|
# Add individual points (semi-transparent)
|
|
for r in all_data[algo]:
|
|
if "error" not in r:
|
|
ax.scatter(
|
|
r["airtime_usage_percent"],
|
|
r["pdr"],
|
|
alpha=0.15,
|
|
color=colors[algo],
|
|
s=20,
|
|
)
|
|
|
|
ax.set_xlabel("Airtime Usage (%)", fontsize=12)
|
|
ax.set_ylabel("Packet Delivery Ratio (%)", fontsize=12)
|
|
ax.set_title(
|
|
"PDR vs Channel Airtime Usage\n(50 seeds per algorithm, error bars = 95% CI)",
|
|
fontsize=14,
|
|
)
|
|
ax.legend(loc="lower right", fontsize=10)
|
|
ax.grid(True, alpha=0.3)
|
|
ax.set_xlim(0, 110)
|
|
ax.set_ylim(0, 35)
|
|
|
|
# Add annotation for efficiency frontier
|
|
ax.annotate(
|
|
"Efficiency\nFrontier",
|
|
xy=(40, 12),
|
|
fontsize=10,
|
|
style="italic",
|
|
bbox=dict(boxstyle="round", facecolor="wheat", alpha=0.5),
|
|
)
|
|
|
|
plt.tight_layout()
|
|
plt.savefig("figures/pdr_vs_airtime.pdf", dpi=300)
|
|
plt.savefig("figures/pdr_vs_airtime.png", dpi=150)
|
|
print("Saved: figures/pdr_vs_airtime.pdf")
|
|
plt.close()
|
|
|
|
|
|
def plot_pdr_vs_tx_cost(all_data, stats):
|
|
"""Generate PDR vs TX Cost plot."""
|
|
fig, ax = plt.subplots(figsize=(8, 6))
|
|
|
|
colors = {"gradient": "#2E86AB", "flooding": "#E94F37", "random": "#7D8491"}
|
|
markers = {"gradient": "o", "flooding": "s", "random": "^"}
|
|
|
|
for algo in ["gradient", "flooding", "random"]:
|
|
algo_stats = stats[algo]
|
|
|
|
pdr_mean = algo_stats["pdr"]["mean"]
|
|
pdr_ci = algo_stats["pdr"]["ci_95"]
|
|
tx_mean = algo_stats["tx_per_success"]["mean"]
|
|
tx_ci = algo_stats["tx_per_success"]["ci_95"]
|
|
|
|
ax.errorbar(
|
|
tx_mean,
|
|
pdr_mean,
|
|
xerr=tx_ci,
|
|
yerr=pdr_ci,
|
|
fmt=markers[algo],
|
|
color=colors[algo],
|
|
markersize=12,
|
|
capsize=5,
|
|
capthick=2,
|
|
linewidth=2,
|
|
label=f"{algo.capitalize()} (PDR: {pdr_mean:.1f}%)",
|
|
)
|
|
|
|
# Add individual points
|
|
for r in all_data[algo]:
|
|
if "error" not in r and r["tx_per_success"] > 0:
|
|
ax.scatter(
|
|
r["tx_per_success"], r["pdr"], alpha=0.15, color=colors[algo], s=20
|
|
)
|
|
|
|
ax.set_xlabel("Transmission Cost (TX per Successful Delivery)", fontsize=12)
|
|
ax.set_ylabel("Packet Delivery Ratio (%)", fontsize=12)
|
|
ax.set_title(
|
|
"PDR vs Transmission Cost\n(50 seeds per algorithm, error bars = 95% CI)",
|
|
fontsize=14,
|
|
)
|
|
ax.legend(loc="lower right", fontsize=10)
|
|
ax.grid(True, alpha=0.3)
|
|
|
|
plt.tight_layout()
|
|
plt.savefig("figures/pdr_vs_tx_cost.pdf", dpi=300)
|
|
plt.savefig("figures/pdr_vs_tx_cost.png", dpi=150)
|
|
print("Saved: figures/pdr_vs_tx_cost.pdf")
|
|
plt.close()
|
|
|
|
|
|
def plot_comparison_bar(stats):
|
|
"""Generate comparison bar chart."""
|
|
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
|
|
|
|
algorithms = ["gradient", "flooding", "random"]
|
|
colors = ["#2E86AB", "#E94F37", "#7D8491"]
|
|
|
|
metrics = [
|
|
("pdr", "PDR (%)"),
|
|
("airtime_usage_percent", "Airtime (%)"),
|
|
("tx_per_success", "TX/Success"),
|
|
]
|
|
|
|
for idx, (metric, label) in enumerate(metrics):
|
|
ax = axes[idx]
|
|
values = [stats[a][metric]["mean"] for a in algorithms]
|
|
errors = [stats[a][metric]["ci_95"] for a in algorithms]
|
|
|
|
bars = ax.bar(
|
|
algorithms, values, yerr=errors, color=colors, capsize=5, alpha=0.8
|
|
)
|
|
ax.set_ylabel(label, fontsize=11)
|
|
ax.set_title(label, fontsize=12)
|
|
ax.grid(True, alpha=0.3, axis="y")
|
|
|
|
# Add value labels
|
|
for bar, val in zip(bars, values):
|
|
ax.text(
|
|
bar.get_x() + bar.get_width() / 2,
|
|
bar.get_height() + errors[algorithms.index(bar.get_x())],
|
|
f"{val:.1f}",
|
|
ha="center",
|
|
va="bottom",
|
|
fontsize=10,
|
|
)
|
|
|
|
plt.tight_layout()
|
|
plt.savefig("figures/comparison_bar.pdf", dpi=300)
|
|
plt.savefig("figures/comparison_bar.png", dpi=150)
|
|
print("Saved: figures/comparison_bar.pdf")
|
|
plt.close()
|
|
|
|
|
|
def main():
|
|
print("Loading results...")
|
|
stats = load_statistics()
|
|
all_data = load_all_results()
|
|
|
|
print("Generating figures...")
|
|
plot_pdr_vs_airtime(all_data, stats)
|
|
plot_pdr_vs_tx_cost(all_data, stats)
|
|
plot_comparison_bar(stats)
|
|
|
|
print("\nAll figures saved to figures/")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|