Files
lora_route_py/sim/experiments/runner.py

232 lines
5.8 KiB
Python

"""
Experiment Runner for LoRa Mesh Simulation.
Provides automated experiment execution with parameter sweeps.
"""
import json
import os
from typing import List, Dict, Any
from itertools import product
from sim.main import run_simulation
def run_single_experiment(
routing: str,
node_count: int,
area_size: float,
sim_time: float,
seed: int = 42,
) -> Dict[str, Any]:
"""
Run a single experiment with given parameters.
Args:
routing: Routing algorithm ("gradient", "flooding", "random")
node_count: Number of nodes
area_size: Area size in meters
sim_time: Simulation time in seconds
seed: Random seed
Returns:
Dictionary with experiment results
"""
results = run_simulation(
num_nodes=node_count,
area_size=area_size,
sim_time=sim_time,
seed=seed,
routing_type=routing,
)
m = results["metrics"]
return {
"routing": routing,
"nodes": node_count,
"area": area_size,
"sim_time": sim_time,
"seed": seed,
"pdr": m["pdr"],
"max_hop": m["max_hop"],
"avg_hop": m["avg_hop"],
"total_sent": m["total_sent"],
"total_received": m["total_received"],
"total_forwarded": m["total_forwarded"],
"collisions": m["collisions"],
"convergence_time": m["convergence_time"],
"route_changes": m["route_changes"],
}
def run_parameter_sweep(
routings: List[str] = None,
node_counts: List[int] = None,
area_sizes: List[float] = None,
sim_time: float = 200,
seeds: List[int] = None,
output_file: str = None,
) -> List[Dict[str, Any]]:
"""
Run parameter sweep experiments.
Args:
routings: List of routing algorithms
node_counts: List of node counts
area_sizes: List of area sizes
sim_time: Simulation time (same for all)
seeds: List of random seeds (for averaging)
output_file: Optional output CSV file
Returns:
List of experiment results
"""
# Default parameters
if routings is None:
routings = ["gradient", "flooding", "random"]
if node_counts is None:
node_counts = [6, 9, 12, 15]
if area_sizes is None:
area_sizes = [500, 800, 1000]
if seeds is None:
seeds = [42, 123, 456] # Multiple seeds for averaging
results = []
# Generate all parameter combinations
total_experiments = len(routings) * len(node_counts) * len(area_sizes) * len(seeds)
current = 0
print(f"Running {total_experiments} experiments...")
for routing, nodes, area, seed in product(routings, node_counts, area_sizes, seeds):
current += 1
print(
f" [{current}/{total_experiments}] {routing}, nodes={nodes}, area={area}, seed={seed}"
)
result = run_single_experiment(
routing=routing,
node_count=nodes,
area_size=area,
sim_time=sim_time,
seed=seed,
)
results.append(result)
# Save to CSV if requested
if output_file:
save_results_csv(results, output_file)
return results
def save_results_csv(results: List[Dict[str, Any]], filename: str):
"""Save experiment results to CSV file."""
import csv
if not results:
return
# Get all keys from first result
keys = list(results[0].keys())
with open(filename, "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=keys)
writer.writeheader()
writer.writerows(results)
print(f"Results saved to {filename}")
def compute_averages(results: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
Compute average results over multiple seeds.
Args:
results: List of experiment results with varying seeds
Returns:
List of averaged results
"""
from collections import defaultdict
# Group by (routing, nodes, area)
groups = defaultdict(list)
for r in results:
key = (r["routing"], r["nodes"], r["area"])
groups[key].append(r)
# Average each group
averaged = []
numeric_keys = [
"pdr",
"max_hop",
"avg_hop",
"total_sent",
"total_received",
"total_forwarded",
"collisions",
"convergence_time",
"route_changes",
]
for key, group in groups.items():
avg_result = {
"routing": key[0],
"nodes": key[1],
"area": key[2],
"num_seeds": len(group),
}
for nk in numeric_keys:
values = [g[nk] for g in group]
avg_result[f"avg_{nk}"] = sum(values) / len(values)
avg_result[f"min_{nk}"] = min(values)
avg_result[f"max_{nk}"] = max(values)
averaged.append(avg_result)
return averaged
def run_quick_comparison(
routing: str = "gradient",
node_count: int = 12,
area_size: float = 800,
sim_time: float = 200,
) -> Dict[str, Any]:
"""
Run a quick comparison of all routing algorithms.
Returns results for gradient, flooding, and random.
"""
results = {}
for r in ["gradient", "flooding", "random"]:
print(f"Running {r}...")
results[r] = run_single_experiment(
routing=r,
node_count=node_count,
area_size=area_size,
sim_time=sim_time,
)
return results
if __name__ == "__main__":
# Quick test
print("Running quick comparison...")
results = run_quick_comparison()
print("\n=== Results ===")
for routing, data in results.items():
print(f"\n{routing.upper()}:")
print(f" PDR: {data['pdr']:.2f}%")
print(f" Max Hop: {data['max_hop']}")
print(f" Avg Hop: {data['avg_hop']:.2f}")
print(f" Sent: {data['total_sent']}, Received: {data['total_received']}")