"""问题一同案配对对比:最优 vs 基线 vs 独立参考解。""" from __future__ import annotations from project.common import (Method, RESULTS_DIR, markdown_table, mean, ratio, write_json, write_text) from project.q1 import baseline, cases, optimal, reference METHODS: tuple[Method, ...] = (optimal.METHOD, baseline.METHOD, reference.METHOD) FINITE = ("point", "segment", "polygon") def _run(case: dict) -> dict: opt = optimal.solve_case(case) base = baseline.solve_case(case) ref = reference.solve_case(case) finite = ref["status"] in FINITE and ref["diameter_m"] is not None return { "id": case["id"], "name": case.get("name", case["id"]), "source": case.get("source", "synthetic"), "measurements": len(case["measurements"]), "status_ref": ref["status"], "status_opt": opt["status"], "status_base": base["status"], "status_opt_ok": opt["status"] == ref["status"], "status_base_ok": base["status"] == ref["status"], "diameter_ref_m": ref["diameter_m"], "diameter_opt_m": opt["diameter_m"], "diameter_base_m": base["diameter_m"], "abs_err_opt_m": (round(abs(opt["diameter_m"] - ref["diameter_m"]), 4) if finite and opt["diameter_m"] is not None else None), "abs_err_base_m": (round(abs(base["diameter_m"] - ref["diameter_m"]), 4) if finite and base["diameter_m"] is not None else None), "covers_ref": ref["covers"], "covers_opt": opt["covers"], "covers_base": base["covers"], "covers_opt_ok": (opt["covers"] == ref["covers"] if ref["covers"] is not None and opt["covers"] is not None else None), "covers_base_ok": (base["covers"] == ref["covers"] if ref["covers"] is not None and base["covers"] is not None else None), "mec_radius_opt_m": opt["mec_radius_m"], "mec_radius_ref_m": ref["mec_radius_m"], "outside_vertices_opt": opt["outside_vertex_count"], "runtime_opt_ms": opt["runtime_ms"], "runtime_base_ms": base["runtime_ms"], "runtime_ref_ms": ref["runtime_ms"], "reference_resolution_m": ref["resolution_m"], } def _aggregate(rows: list[dict]) -> dict: polygon_rows = [r for r in rows if r["status_ref"] == "polygon"] opt_errs = [r["abs_err_opt_m"] for r in polygon_rows if r["abs_err_opt_m"] is not None] base_errs = [r["abs_err_base_m"] for r in polygon_rows if r["abs_err_base_m"] is not None] cov_opt = [r["covers_opt_ok"] for r in rows if r["covers_opt_ok"] is not None] cov_base = [r["covers_base_ok"] for r in rows if r["covers_base_ok"] is not None] return { "cases": len(rows), "diameter_compared": len(opt_errs), "status_accuracy_optimal": ratio(sum(r["status_opt_ok"] for r in rows), len(rows)), "status_accuracy_baseline": ratio(sum(r["status_base_ok"] for r in rows), len(rows)), "diameter_mae_optimal_m": mean(opt_errs), "diameter_mae_baseline_m": mean(base_errs), "diameter_max_err_optimal_m": round(max(opt_errs), 4) if opt_errs else 0.0, "diameter_max_err_baseline_m": round(max(base_errs), 4) if base_errs else 0.0, "coverage_compared": len(cov_opt), "coverage_accuracy_optimal": ratio(sum(cov_opt), len(cov_opt)), "coverage_accuracy_baseline": ratio(sum(cov_base), len(cov_base)), "runtime_optimal_mean_ms": mean(r["runtime_opt_ms"] for r in rows), "runtime_baseline_mean_ms": mean(r["runtime_base_ms"] for r in rows), "runtime_reference_mean_ms": mean(r["runtime_ref_ms"] for r in rows), } def _render(rows: list[dict], aggregate: dict, title: str) -> str: headers = ["算例", "来源", "参考状态", "最优状态", "基线状态", "D参考(m)", "D最优(m)", "D基线(m)", "|ΔD|最优", "|ΔD|基线", "覆盖参考", "覆盖最优", "覆盖基线"] body = [] for r in rows: body.append([ r["name"], r["source"], r["status_ref"], r["status_opt"], r["status_base"], _fmt(r["diameter_ref_m"]), _fmt(r["diameter_opt_m"]), _fmt(r["diameter_base_m"]), _fmt(r["abs_err_opt_m"]), _fmt(r["abs_err_base_m"]), _bool(r["covers_ref"]), _bool(r["covers_opt"]), _bool(r["covers_base"]), ]) text = [f"# {title}", "", "参考解为独立的大框半平面裁剪 + 精确直径/MEC;", "固定示例见 `examples/q1`,解析期望由 `tests/test_geometry*.py` 固化。", "", markdown_table(headers, body), "", "## 汇总", ""] summary_rows = [ ["状态判定准确率", _pct(aggregate["status_accuracy_optimal"]), _pct(aggregate["status_accuracy_baseline"])], ["直径平均绝对误差 (m)", _fmt(aggregate["diameter_mae_optimal_m"], 3), _fmt(aggregate["diameter_mae_baseline_m"], 3)], ["直径最大误差 (m)", _fmt(aggregate["diameter_max_err_optimal_m"], 3), _fmt(aggregate["diameter_max_err_baseline_m"], 3)], ["覆盖判定准确率", _pct(aggregate["coverage_accuracy_optimal"]), _pct(aggregate["coverage_accuracy_baseline"])], ["平均耗时 (ms)", _fmt(aggregate["runtime_optimal_mean_ms"], 3), _fmt(aggregate["runtime_baseline_mean_ms"], 3)], ] text.append(markdown_table(["指标", "最优", "基线"], summary_rows)) return "\n".join(text) + "\n" def _fmt(value, digits: int = 2) -> str: if value is None: return "—" if isinstance(value, bool): return "是" if value else "否" if isinstance(value, float): return f"{value:.{digits}f}" return str(value) def _bool(value) -> str: return "—" if value is None else ("是" if value else "否") def _pct(value: float) -> str: return f"{value*100:.1f}%" def run(quick: bool = False) -> dict: example_rows = [_run(case) for case in cases.load_examples()] random_rows = [_run(case) for case in cases.random_cases(12 if quick else 40)] payload = { "question": 1, "methods": [m.__dict__ for m in METHODS], "examples": example_rows, "random": random_rows, "aggregate_examples": _aggregate(example_rows), "aggregate_random": _aggregate(random_rows), } write_json(RESULTS_DIR / "q1.json", payload) text = _render(example_rows, payload["aggregate_examples"], "问题一:固定示例对比") text += "\n" + _render(random_rows, payload["aggregate_random"], "问题一:随机交会算例对比") write_text(RESULTS_DIR / "q1.md", text) return payload