Files
2026-09-12 16:58:25 +08:00

149 lines
6.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""统一入口:一次跑完 1-4 问的"最优 vs 基线"对比并生成 results/。
用法(仓库根目录)::
uv run python -m project.run # 四问全部
uv run python -m project.run --question 3 # 只跑第三问
uv run python -m project.run --quick # 缩小算例/基准集规模
"""
from __future__ import annotations
import argparse
import time
from project.common import RESULTS_DIR, markdown_table, write_text
from project.q1 import compare as q1_compare
from project.q2 import compare as q2_compare
from project.q3 import compare as q3_compare
from project.q4 import compare as q4_compare
RUNNERS = {
1: q1_compare.run,
2: q2_compare.run,
3: q3_compare.run,
4: q4_compare.run,
}
STRUCTURE = """\
project/
├── common.py 公共层:方法描述/指标聚合/结果落盘/闭环装配
├── q1/ 问题一:直径与直径圆覆盖
│ ├── optimal.py 生产实现:半平面交 + 顶点对直径 + MEC
│ ├── baseline.py 基线:网格采样 + bbox 对角线代理
│ ├── reference.py 参考:大框半平面裁剪 + 精确直径/MEC
│ ├── cases.py 固定示例(examples/q1)+ 随机交会算例
│ └── compare.py 同案配对对比
├── q2/ 问题二:第二检测点选择
│ ├── optimal.py 生产实现:保证接收盘内最小最坏后验半径
│ ├── baseline.py 基线:垂直 1000 m / MEC 圆心
│ ├── reference.py 参考:保证接收盘内网格枚举
│ ├── region.py K1 统一构造
│ ├── cases.py 随机首次示向度算例
│ └── compare.py
├── q3/ 问题三:全向闭环
│ ├── optimal.py bayes(生产默认策略)
│ ├── baseline.py grid(49 点网格)/ seven(七点批量)
│ └── compare.py
├── q4/ 问题四:混合源闭环
│ ├── optimal.py directional(定向扩展)
│ ├── baseline.py bayes / grid 直接搬用
│ └── compare.py
├── run.py 统一入口(本文件)
└── results/ 自动生成:q1.md/json … q4.md/json + summary.md
"""
def _q1_headline(payload: dict) -> list[str]:
agg = payload["aggregate_random"]
return ["1", "直径平均误差 / 覆盖准确率",
f"{agg['diameter_mae_optimal_m']:.2f} m / "
f"{agg['coverage_accuracy_optimal']*100:.0f}%",
f"{agg['diameter_mae_baseline_m']:.2f} m / "
f"{agg['coverage_accuracy_baseline']*100:.0f}%"]
def _q2_headline(payload: dict) -> list[str]:
agg = payload["aggregate"]
opt = agg["optimal"]
base = agg["baseline_mec"]
return ["2", "保证接收率 / 最坏后验均值",
f"{opt['guaranteed_rate']*100:.0f}% / "
f"{opt['worst_posterior_mean_m']:.1f} m",
f"{base['guaranteed_rate']*100:.0f}% / "
f"{base['worst_posterior_mean_m']:.1f} m"]
def _q3_headline(payload: dict) -> list[str]:
agg = payload["aggregate"]["variants"]
opt, base = agg["optimal"], agg["baseline"]
paired = payload["aggregate"]["paired"].get("optimal_vs_baseline", {})
return ["3", "全清率 / T 均值(相对基线)",
f"{opt['cleared_ratio_mean']*100:.0f}% / "
f"{opt['virtual_time_mean_s']:.0f} s "
f"({paired.get('mean_relative_change', 0)*100:+.0f}%)",
f"{base['cleared_ratio_mean']*100:.0f}% / "
f"{base['virtual_time_mean_s']:.0f} s"]
def _q4_headline(payload: dict) -> list[str]:
agg = payload["aggregate"]["variants"]
opt, base = agg["optimal"], agg["baseline"]
return ["4", "全清率 / 缺席误判(问题三策略搬用)",
f"{opt['cleared_ratio_mean']*100:.0f}% / "
f"{opt['absent_misjudged_total']}",
f"{base['cleared_ratio_mean']*100:.0f}% / "
f"{base['absent_misjudged_total']}"]
def _write_summary(payloads: dict[int, dict]) -> None:
lines = ["# 1-4 问最优 vs 基线:对比总览", "",
"本节所有数字由 `uv run python -m project.run` 在同机同口径下生成,",
"逐问明细见 `q1.md` … `q4.md`。", ""]
rows = []
if 1 in payloads:
rows.append(_q1_headline(payloads[1]))
if 2 in payloads:
rows.append(_q2_headline(payloads[2]))
if 3 in payloads:
rows.append(_q3_headline(payloads[3]))
if 4 in payloads:
rows.append(_q4_headline(payloads[4]))
lines.append(markdown_table(["问", "核心指标", "最优实现", "基线实现"], rows))
lines += ["", "## 方法映射", "",
"| 问 | 最优实现 | 基线实现 | 独立参考 |",
"| --- | --- | --- | --- |",
"| 1 | 半平面交 + 顶点对直径 + MEC | 网格采样 + bbox 对角线代理 | 大框半平面裁剪 + 精确直径/MEC |",
"| 2 | 保证接收盘内最小最坏后验半径 | 垂直 1000 m / MEC 圆心 | 保证接收盘内网格枚举 |",
"| 3 | bayes(粒子滤波 + 信息增益 + 滚动调度) | grid(49 点网格)/ seven(七点批量) | 冻结基准集 + 逐微秒计时复算 |",
"| 4 | directional(联合信念 + 后备清除) | bayes / grid 直接搬用 | 冻结基准集 + 真值全清校验 |",
"", "## 目录结构", "", "```text", STRUCTURE.rstrip(), "```", ""]
write_text(RESULTS_DIR / "summary.md", "\n".join(lines))
def main(argv=None) -> int:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--question", choices=("1", "2", "3", "4", "all"),
default="all")
parser.add_argument("--quick", action="store_true",
help="缩小随机算例数/冻结基准集规模")
args = parser.parse_args(argv)
selected = [1, 2, 3, 4] if args.question == "all" else [int(args.question)]
payloads: dict[int, dict] = {}
for question in selected:
start = time.perf_counter()
print(f"[Q{question}] 运行对比 …", flush=True)
payloads[question] = RUNNERS[question](quick=args.quick)
print(f"[Q{question}] 完成,用时 {time.perf_counter() - start:.1f}s",
flush=True)
if selected == [1, 2, 3, 4]:
_write_summary(payloads)
print(f"总览已写入 {RESULTS_DIR / 'summary.md'}")
print(f"结果目录:{RESULTS_DIR}")
return 0
if __name__ == "__main__":
raise SystemExit(main())