traffix-live / scripts /run_experiments.py
REBELLION2002's picture
Deploy live TraffiX app (fix/live-container-ci e8a6317, showcase network omitted)
ec5ee81 verified
Raw History Blame Contribute Delete
4.66 kB
"""
Runs controllers x scenarios x seeds and saves results/results.csv.
python scripts/run_experiments.py
python scripts/run_experiments.py --scenario emergency --controller traffix --seeds 1
python scripts/run_experiments.py --gui --scenario rain --controller traffix --seeds 1
Results are saved after every run. If it stops (crash, Ctrl+C, laptop off), run the same
command again: finished runs are skipped. Use --fresh to start over.
"""
import argparse
import csv
import os
from multiprocessing import Pool
import _path # noqa: F401
from traffix.core import config
def job(args):
"""Runs one simulation. A failure is returned as a row (never raised), so one bad run
can't stop the batch, and the error message survives the trip back from the worker."""
import traceback
from traffix.evaluation.runner import run_one
try:
return run_one(*args)
except Exception as e:
c, s, seed, _ = args
return {"controller": c, "scenario": s, "seed": seed,
"error": f"{e.__class__.__name__}: {e}", "traceback": traceback.format_exc()[-1500:]}
def done_keys(path):
"""Runs already saved in the results file (for resuming)."""
if not os.path.exists(path):
return set(), []
with open(path, newline="") as f:
rows = [r for r in csv.DictReader(f) if not r.get("error")]
return {(r["controller"], r["scenario"], int(r["seed"])) for r in rows}, rows
def save(rows, path):
keys = list(dict.fromkeys(k for r in rows for k in r))
tmp = path + ".tmp"
with open(tmp, "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=keys)
w.writeheader()
w.writerows(rows)
os.replace(tmp, path)
if __name__ == "__main__":
sim, sc = config.simulation(), config.scenarios()
p = argparse.ArgumentParser()
p.add_argument("--controller", nargs="+", default=sim["controllers"], choices=sim["controllers"])
p.add_argument("--scenario", nargs="+", default=list(sc), choices=list(sc))
p.add_argument("--seeds", nargs="+", type=int, default=sim["seeds"])
p.add_argument("--workers", type=int, default=sim["workers"])
p.add_argument("--gui", action="store_true")
p.add_argument("--out", default=config.path("results", "results.csv"))
p.add_argument("--fresh", action="store_true", help="ignore existing results and start over")
a = p.parse_args()
os.makedirs(os.path.dirname(a.out), exist_ok=True)
if a.fresh and os.path.exists(a.out):
os.remove(a.out)
done, rows = done_keys(a.out)
jobs = [(c, s, seed, a.gui) for s in a.scenario for c in a.controller for seed in a.seeds
if (c, s, seed) not in done]
from traffix.evaluation.runner import GEN, demand_is_current
stale = sorted({(s, seed) for _, s, seed, _ in jobs
if not demand_is_current(os.path.join(GEN, f"{s}_seed{seed}"))})
if stale: # traffic missing or built for another map
from traffix.demand.generator import DemandGenerator
print(f"Building traffic for the {config.network_name()} map ({len(stale)} scenario/seed pairs)")
gen = DemandGenerator(config.network_files()[0])
for s, seed in stale:
gen.build(s, sc[s], seed, GEN)
print(f"Running {len(jobs)} simulations" + (f" ({len(done)} already saved in {a.out}, skipped)" if done else ""))
failures = []
if a.gui or a.workers <= 1:
results = map(job, jobs)
else:
results = Pool(a.workers).imap(job, jobs)
for r in results:
if r.get("error"):
failures.append(r)
print(f"{r['scenario']:18s} {r['controller']:14s} seed {r['seed']}: FAILED {r['error']}", flush=True)
with open(os.path.join(os.path.dirname(a.out), "failed_runs.log"), "a") as f:
f.write(f"{r['controller']} {r['scenario']} seed {r['seed']}\n{r['traceback']}\n\n")
continue
rows.append(r)
save(rows, a.out) # saved after every run
print(f"{r['scenario']:18s} {r['controller']:14s} seed {r['seed']}: car wait "
f"{r['veh_mean_wait_s']:6.1f}s ped wait {r['ped_mean_wait_s']:6.1f}s "
f"unfinished {r['veh_unfinished']:3d} truncations {r['clearance_truncations']:4d}", flush=True)
print(f"Saved {len(rows)} runs to {a.out}")
if failures:
print(f"{len(failures)} run(s) failed; details in results/failed_runs.log. "
f"Rerun the same command to retry only those.")