114 lines
5.0 KiB
Python
114 lines
5.0 KiB
Python
"""问题3/4 RL 策略与既有策略的配对自建消融。
|
|
|
|
同一种子、同一场景分别跑基线(Q3 bayes / Q4 directional)与 RL 变体,
|
|
比较全部清除成功、虚拟总时间、检测次数与请求总数。结果写入
|
|
output/rl/benchmark.json。
|
|
|
|
用法:
|
|
uv sync --group rl --group dev
|
|
uv run python scripts/benchmark_rl.py --seeds 5
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import statistics
|
|
import time
|
|
from pathlib import Path
|
|
|
|
from mcm26b.config import ProblemConfig
|
|
from mcm26b.harness import (LocalSimTransport, mixed_scene, random_omni_scene,
|
|
referee_spec)
|
|
from mcm26b.planner import DirectionalExtension
|
|
from mcm26b.planner.bayes_loop import BayesLoop
|
|
from mcm26b.planner.rl_loop import RLBayesLoop, RLDirectionalLoop
|
|
from mcm26b.protocol.client import SimulatorClient
|
|
from referee import LocalSimulator
|
|
|
|
|
|
def run_one(loop_cls, sources, *, config, robot_id: str, **kwargs) -> dict:
|
|
simulator = LocalSimulator(sources, robot_id=robot_id,
|
|
spec=referee_spec(config, max_real_duration_s=3600.0),
|
|
stress_mode=True)
|
|
client = SimulatorClient(robot_id, config=config,
|
|
transport=LocalSimTransport(simulator))
|
|
started = time.monotonic()
|
|
summary = loop_cls(client, config, **kwargs).run() \
|
|
if kwargs else loop_cls(client, config).run()
|
|
return {
|
|
"completed": bool(summary["completed"]),
|
|
"virtual_time_s": float(summary["virtual_time_s"]),
|
|
"measures": int(summary["measures"]),
|
|
"total_requests": int(summary["total_requests"]),
|
|
"move_distance_m": float(summary["move_distance_m"]),
|
|
"wall_s": round(time.monotonic() - started, 3),
|
|
}
|
|
|
|
|
|
def main(argv=None) -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--seeds", type=int, default=5)
|
|
parser.add_argument("--sources", type=int, default=10)
|
|
parser.add_argument("--directional", type=int, default=3)
|
|
parser.add_argument("--output", default="output/rl/benchmark.json")
|
|
args = parser.parse_args(argv)
|
|
config = ProblemConfig()
|
|
robot_id = "2026001"
|
|
report = {"config": {"seeds": args.seeds, "sources": args.sources,
|
|
"directional": args.directional},
|
|
"q3": [], "q4": []}
|
|
|
|
for seed in range(20260910, 20260910 + args.seeds):
|
|
omni = random_omni_scene(seed=seed, count=args.sources, config=config)
|
|
mixed = mixed_scene(seed=seed, count=args.sources,
|
|
directional=args.directional, config=config)
|
|
q3_base = run_one(BayesLoop, omni, config=config, robot_id=robot_id)
|
|
q3_rl = run_one(RLBayesLoop, omni, config=config, robot_id=robot_id)
|
|
q4_base = run_one(DirectionalExtension, mixed, config=config,
|
|
robot_id=robot_id)
|
|
q4_rl = run_one(RLDirectionalLoop, mixed, config=config,
|
|
robot_id=robot_id)
|
|
report["q3"].append({"seed": seed, "bayes": q3_base, "rl": q3_rl})
|
|
report["q4"].append({"seed": seed, "directional": q4_base, "rl": q4_rl})
|
|
print(json.dumps({"event": "seed_done", "seed": seed,
|
|
"q3_base_s": q3_base["virtual_time_s"],
|
|
"q3_rl_s": q3_rl["virtual_time_s"],
|
|
"q4_base_s": q4_base["virtual_time_s"],
|
|
"q4_rl_s": q4_rl["virtual_time_s"]},
|
|
ensure_ascii=False))
|
|
|
|
for key, baseline in (("q3", "bayes"), ("q4", "directional")):
|
|
rows = report[key]
|
|
deltas = [row["rl"]["virtual_time_s"] - row[baseline]["virtual_time_s"]
|
|
for row in rows]
|
|
cleared = all(row["rl"]["completed"] and row[baseline]["completed"]
|
|
for row in rows)
|
|
report[key + "_summary"] = {
|
|
"mean_virtual_s": {
|
|
baseline: statistics.mean(row[baseline]["virtual_time_s"] for row in rows),
|
|
"rl": statistics.mean(row["rl"]["virtual_time_s"] for row in rows),
|
|
},
|
|
"mean_delta_s": statistics.mean(deltas),
|
|
"relative_delta_pct": 100.0 * statistics.mean(deltas)
|
|
/ statistics.mean(row[baseline]["virtual_time_s"] for row in rows),
|
|
"mean_measures": {
|
|
baseline: statistics.mean(row[baseline]["measures"] for row in rows),
|
|
"rl": statistics.mean(row["rl"]["measures"] for row in rows),
|
|
},
|
|
"all_cleared": cleared,
|
|
}
|
|
output = Path(args.output)
|
|
output.parent.mkdir(parents=True, exist_ok=True)
|
|
output.write_text(json.dumps(report, ensure_ascii=False, indent=2),
|
|
encoding="utf-8")
|
|
print(json.dumps({"event": "benchmark_done", "path": str(output),
|
|
"q3": report["q3_summary"],
|
|
"q4": report["q4_summary"]},
|
|
ensure_ascii=False, indent=2))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|