"""Dedup-by-tid aggregate. Handles both live eval*.log and finished eval*.log.gz.""" import re import gzip import glob import os EVALS = "/imotion-vepfs/imotion_zxnet/workspace_wei/ry-dynamics-vln-ryworld/checkpoints/ryworld_stage1_discrete_20260510_025317_cw07_iw25/evals" pat = re.compile( r'"shortest_path_length":\s*([\d.]+).*?"NE":\s*([\d.]+).*?"success":\s*([\d.]+).*?' r'"osr":\s*([\d.]+).*?"TL":\s*([\d.]+).*?"spl":\s*([\d.]+).*?"ndtw":\s*([\d.]+).*?' r'"trajectory_id":\s*"([^"]+)"', re.S, ) all_eps = {} log_paths = glob.glob(f"{EVALS}/*full835*/eval*.log") + glob.glob(f"{EVALS}/*full835*/eval*.log.gz") for p in sorted(log_paths): if p.endswith(".gz"): content = gzip.open(p, "rt").read() else: content = open(p).read() for m in pat.finditer(content): spg, ne, succ, osr, tl, spl, ndtw, tid = m.groups() all_eps[tid] = { "spg": float(spg), "NE": float(ne), "success": int(float(succ) == 1.0), "osr": int(float(osr) == 1.0), "SPL": float(spl), "nDTW": float(ndtw), } n = len(all_eps) if n == 0: print("no eps found") raise SystemExit eps = list(all_eps.values()) def avg(field): return sum(e[field] for e in eps) / n print(f"=== TOTAL {n}/835 ({100*n/835:.1f}%) ===") print(f" SR={100*avg('success'):.1f}% OSR={100*avg('osr'):.1f}% NE={avg('NE'):.2f} SPL={100*avg('SPL'):.1f}% nDTW={avg('nDTW'):.3f}") for lo, hi in [(0, 5), (5, 8), (8, 12), (12, 18)]: bk = [e for e in eps if lo <= e["spg"] < hi] if bk: b_sr = sum(e["success"] for e in bk) / len(bk) print(f" short [{lo:>2},{hi:>2})m: n={len(bk):>3} SR={100*b_sr:>4.1f}%")