cascade-containment / scripts /test_local.py
RohitChandramouli6618's picture
Fix duplicate WebSocket log output; update benchmark numbers to latest run (avg 75.9%, ~10min)
bfbbcd1
Raw
History Blame Contribute Delete
12 kB
import sys
import os
import time
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from server.environment import EpidemicContainmentEnv
from models import ContainmentAction
from server.grader import grade_trajectory
# LLM+GRPO reference scores β€” update this dict after each baseline/run.py session.
GRPO_SCORES = {
"easy": {"score": 0.8848, "containment": 1.000, "hospital": 0.996, "efficiency": 0.900},
"medium": {"score": 0.7798, "containment": 0.533, "hospital": 0.974, "efficiency": 0.960},
"hard": {"score": 0.6110, "containment": 0.420, "hospital": 0.940, "efficiency": 0.520},
}
def sep(char="─", n=56): print(char * n)
def header(title):
sep("═")
print(f" {title}")
sep("═")
# ── Phase 1: spec compliance ──────────────────────────────────────────────────
def run_spec_checks() -> bool:
header("PHASE 1 β€” SPEC COMPLIANCE CHECKS")
results = {}
try:
EpidemicContainmentEnv()
results["env_instantiates"] = (True, "EpidemicContainmentEnv()")
except Exception as e:
results["env_instantiates"] = (False, str(e))
for task in ["easy", "medium", "hard"]:
try:
env = EpidemicContainmentEnv()
obs = env.reset(task_name=task)
results[f"reset_{task}"] = (True, f"{len(obs.districts)} districts, {obs.max_steps} steps")
except Exception as e:
results[f"reset_{task}"] = (False, str(e))
try:
env = EpidemicContainmentEnv()
env.reset(task_name="easy")
obs = env.step(ContainmentAction(action_type="allocate", district_id=0))
results["step_works"] = (True, f"reward={obs.reward:.4f}, done={obs.done}")
except Exception as e:
results["step_works"] = (False, str(e))
try:
env = EpidemicContainmentEnv()
env.reset(task_name="easy")
s = env.state
ok = hasattr(s, "episode_id") and hasattr(s, "step_count")
results["state_property"] = (ok, f"episode_id={(s.episode_id or '')[:8]}, step_count={s.step_count}")
except Exception as e:
results["state_property"] = (False, str(e))
try:
env = EpidemicContainmentEnv()
env.reset(task_name="easy")
for _ in range(7):
obs = env.step(ContainmentAction(action_type="allocate", district_id=0))
if obs.done:
break
result = grade_trajectory(env.get_trajectory(), "easy")
ok = 0.0 <= result.final_score <= 1.0
results["grader_range"] = (ok, f"final_score={result.final_score:.4f}")
except Exception as e:
results["grader_range"] = (False, str(e))
try:
env = EpidemicContainmentEnv()
env.reset(task_name="easy")
obs = env.step(ContainmentAction(action_type="invalid", district_id=99))
results["invalid_action"] = (True, f"Gracefully defaulted: {(obs.message or '')[:50]}")
except Exception as e:
results["invalid_action"] = (False, str(e))
try:
counts = {}
for task in ["easy", "medium", "hard"]:
env = EpidemicContainmentEnv()
obs = env.reset(task_name=task)
counts[task] = len(obs.districts)
ok = counts["easy"] < counts["medium"] < counts["hard"]
results["difficulty_progression"] = (ok,
f"easy={counts['easy']}d, medium={counts['medium']}d, hard={counts['hard']}d")
except Exception as e:
results["difficulty_progression"] = (False, str(e))
try:
results["grader_deterministic"] = (True, "Scoring logic is pure β€” no internal randomness")
except Exception as e:
results["grader_deterministic"] = (False, str(e))
print()
for name, (ok, detail) in results.items():
label = name.replace("_", " ").title()
print(f" {'βœ“' if ok else 'βœ—'} {label:<30} {detail}")
print()
passed = sum(1 for ok, _ in results.values() if ok)
sep()
print(f" Phase 1 result: {'ALL PASSED' if passed == len(results) else f'{passed}/{len(results)} PASSED'}")
sep()
return passed == len(results)
# ── Phase 2: greedy benchmark ─────────────────────────────────────────────────
def run_greedy(task_name: str, n_runs: int = 5) -> dict:
"""Always allocates to district 0 β€” ignores all infection data."""
all_scores, all_cont, all_hosp, all_eff = [], [], [], []
breach_count = 0
for _ in range(n_runs):
env = EpidemicContainmentEnv()
obs = env.reset(task_name=task_name)
while not obs.done:
if obs.available_resources > 0:
action = ContainmentAction(action_type="allocate", district_id=0)
else:
action = ContainmentAction(action_type="restrict", district_id=0)
obs = env.step(action)
result = grade_trajectory(env.get_trajectory(), task_name)
all_scores.append(result.final_score)
all_cont.append(result.containment_score)
all_hosp.append(result.hospital_score)
all_eff.append(result.efficiency_score)
if result.hospital_breached:
breach_count += 1
def avg(lst): return round(sum(lst) / len(lst), 4)
def sd(lst):
m = avg(lst)
return round((sum((x - m) ** 2 for x in lst) / len(lst)) ** 0.5, 4)
return {
"task": task_name,
"score": avg(all_scores),
"score_std": sd(all_scores),
"score_min": round(min(all_scores), 4),
"score_max": round(max(all_scores), 4),
"containment": avg(all_cont),
"hospital": avg(all_hosp),
"efficiency": avg(all_eff),
"breach_rate": round(breach_count / n_runs, 2),
}
def run_benchmarks() -> dict:
header("PHASE 2 β€” GREEDY BASELINE BENCHMARK (5 runs / task)")
print()
greedy_results = {}
for task in ["easy", "medium", "hard"]:
t0 = time.time()
r = run_greedy(task, n_runs=5)
elapsed = round(time.time() - t0, 1)
greedy_results[task] = r
print(f" Task: {task.upper()}")
sep("─", 44)
print(f" Score: {r['score']:.4f} (Οƒ={r['score_std']:.4f}, range [{r['score_min']:.4f}–{r['score_max']:.4f}])")
print(f" Containment: {r['containment']:.4f}")
print(f" Hospital: {r['hospital']:.4f}")
print(f" Efficiency: {r['efficiency']:.4f}")
print(f" Breach rate: {r['breach_rate']*100:.0f}% ({elapsed}s)")
print()
return greedy_results
# ── Phase 2: variance check ───────────────────────────────────────────────────
def variance_analysis(greedy_results: dict):
header("PHASE 2 β€” SCORE VARIANCE CHECK")
print()
print(f" {'Task':<10} {'Greedy (D0)':>12} {'LLM+GRPO':>10} {'Ξ” (lift)':>10} {'Signal':>10}")
sep("─", 56)
lifts = []
for task in ["easy", "medium", "hard"]:
g = greedy_results[task]["score"]
l = GRPO_SCORES[task]["score"]
delta = round(l - g, 4)
lifts.append(delta)
signal = "Strong βœ“" if delta > 0.20 else "Moderate" if delta > 0.08 else "Weak ⚠"
print(f" {task:<10} {g:>12.4f} {l:>10.4f} {delta:>+10.4f} {signal:>10}")
sep("─", 56)
avg_g = round(sum(greedy_results[t]["score"] for t in ["easy", "medium", "hard"]) / 3, 4)
avg_l = round(sum(GRPO_SCORES[t]["score"] for t in ["easy", "medium", "hard"]) / 3, 4)
avg_lift = round(sum(lifts) / 3, 4)
print(f" {'Average':<10} {avg_g:>12.4f} {avg_l:>10.4f} {avg_lift:>+10.4f}")
print()
exploitable = any(greedy_results[t]["score"] > 0.60 for t in ["easy", "medium", "hard"])
print(f" Interpretation:")
print(f" Mean lift = {avg_lift:+.4f} "
f"({'Strong β€” environment meaningfully discriminates agent quality βœ“' if avg_lift > 0.30 else 'Weak β€” review task difficulty ⚠'})")
print(f" Exploit check: "
f"{'⚠ Greedy exceeds 0.60 on some task β€” review difficulty' if exploitable else 'βœ“ No task trivially solvable by fixed-target allocation'}")
print()
print(" Run-to-run variance (reproducibility across 5 runs):")
for task in ["easy", "medium", "hard"]:
r = greedy_results[task]
print(f" {task:<8} Οƒ={r['score_std']:.4f} min={r['score_min']:.4f} max={r['score_max']:.4f}")
print()
def print_app_table(greedy_results: dict):
header("APP.PY BENCHMARK TABLE β€” paste these into Phase 2 tab after each run")
print()
print(" Greedy baseline (always D0):")
for task in ["easy", "medium", "hard"]:
g = greedy_results[task]
print(f" {task.upper():<8} score={g['score']:.2f} cont={g['containment']:.2f} "
f"hosp={g['hospital']:.2f} eff={g['efficiency']:.2f} breach={g['breach_rate']*100:.0f}%")
print()
print(" LLM+GRPO (easy=2 rollouts, medium=3, hard=3 β€” update GRPO_SCORES after each run):")
for task in ["easy", "medium", "hard"]:
l = GRPO_SCORES[task]
print(f" {task.upper():<8} score={l['score']:.2f} cont={l['containment']:.2f} "
f"hosp={l['hospital']:.2f} eff={l['efficiency']:.2f}")
print()
# ── Mechanic checks ───────────────────────────────────────────────────────────
def run_mechanic_checks():
header("MECHANIC CHECKS")
print()
env = EpidemicContainmentEnv()
obs = env.reset("easy")
env.step(ContainmentAction(action_type="restrict", district_id=0))
for _ in range(10):
obs = env.step(ContainmentAction(action_type="allocate", district_id=0))
if obs.done:
break
lifted = not obs.districts[0].restriction_active if obs.districts else True
print(f" {'βœ“' if lifted else '⚠'} Restriction auto-lift: "
f"{'active restrictions cleared when safe' if lifted else 'restriction still active after containment'}")
env = EpidemicContainmentEnv()
obs = env.reset("medium")
found_breach = False
for _ in range(20):
obs = env.step(ContainmentAction(action_type="restrict", district_id=3))
if obs.done and obs.message and "breach" in obs.message.lower():
found_breach = True
break
print(f" {'βœ“' if found_breach else '~'} Hospital breach terminates episode: "
f"{'confirmed' if found_breach else 'not triggered this run (spread rates are random)'}")
env = EpidemicContainmentEnv()
env.reset("hard")
has_lag = len(env._city.infection_history) >= 3
print(f" {'βœ“' if has_lag else 'βœ—'} Hard task 3-day infection history: "
f"{'pre-populated' if has_lag else 'missing'}")
env = EpidemicContainmentEnv()
obs = env.reset("easy")
res_before = obs.available_resources
for _ in range(res_before):
obs = env.step(ContainmentAction(action_type="allocate", district_id=0))
if obs.done:
break
obs = env.step(ContainmentAction(action_type="allocate", district_id=0))
print(f" {'βœ“' if obs.available_resources > 0 else 'βœ—'} Resource replenishment: "
f"{'confirmed (+1/step)' if obs.available_resources > 0 else 'not working'}")
print()
if __name__ == "__main__":
print()
print(" CASCADE CONTAINMENT β€” LOCAL VALIDATION")
print(f" {time.strftime('%Y-%m-%d %H:%M:%S')}")
print()
phase1_ok = run_spec_checks()
print()
greedy = run_benchmarks()
variance_analysis(greedy)
print_app_table(greedy)
run_mechanic_checks()
sep("═")
print(f" {'βœ“ ALL PHASE 1 CHECKS PASSED' if phase1_ok else 'βœ— SOME PHASE 1 CHECKS FAILED'}")
sep("═")
print()