43 KiB
第三、四问系统性架构优化与运行提速方案
- 版本:2026-09-12
- 依据:本仓库运行历史(
session.jsonl、practice-p3-*.jlog、referee-data/sessions/*、output/hrl/ui/q3-main/*、docs/全部性能记录)+ 本文档新增的复跑与离线校核实验 - 范围:第三问(全向源)与第四问(混合定向源)的闭环架构、算法分解、运行治理, 不改动题面约束、协议层语义与既有保守性不变量
- 结论先行:虚拟时间的 75–83% 是移动,移动里约一半不是覆盖巡回而是定位/清除往返; 第四问的全部失败来自定向源;旧机器学习层已被证明不如手写基线。 因此本轮优化的主线是"把定位并入覆盖巡回 + 把发现集压到最小命中集 + 给定向源建位置×朝向联合信念", 而不是再堆学习算法。
0. 摘要(TL;DR)
0.1 三个待解的结构性浪费
| 编号 | 现象 | 直接证据 | 估计可回收 |
|---|---|---|---|
| W1 | Q3 定位探点往返 5741 m / 1148 s,与覆盖巡回(6960 m / 1392 s)相当 | 本次复跑(13 源,seed 0):24 次探点,均值 442 m/次 | −900 ~ −1100 s/局 |
| W2 | Q4 发现集是 700 m 方格 49 点,巡回 36.4 km;同一保证下贪心最小命中集只要 31 点、22.9 km | scripts/verify_q4_cover.py:43 032 个 (p,h) 采样,两组点集均 100% 覆盖 |
−2700 s(巡回)+ −1140 s(检测) |
| W3 | Q4 定向源没有"位置×朝向"联合信念,只能阶梯探点,最后退化到 ≤400 格盲清 | referee 两局:全向 17/17 清除,定向仅 2/6;4/4 漏检全是定向 | −1500 ~ −3000 s/局 |
0.2 预期收益(自建模拟器,同一冻结基准集,配对口径)
| 指标 | Q3 现状 | Q3 目标 | Q4 现状 | Q4 目标 |
|---|---|---|---|---|
| 平均虚拟时间 | 3707 s(本次 6 场景)/ 3536 s(13 源单局) | ≤ 2700 s | 13 652 s(10 源 3 定向单局)/ 13 165 s(4 场景均值) | ≤ 9000 s |
| 最大虚拟时间 | 4628 s | ≤ 4000 s | > 17 000 s(边界压力) | ≤ 12 000 s |
| 每局计算墙钟 | 2.9–6.5 s | ≤ 0.8 s | 6.5 s | ≤ 1.0 s |
| 有效局(全清+缺席无误判+零异常+正常退出+计时复算一致) | 6/6 | 基准集 100% | 4/4 | 基准集 100% |
4000 s 是本项目
docs/算法审核与优化-20260911.md自设的验收线,不是题面评分线; 第四问在 10 源场景下不承诺达成 4000 s,理由见 §6.2。
0.3 明确不做的事
- 不重新引入 PPO/HRL。运行历史支持"保留手写基线":HRL 训练 4007.9 s / 109 776 步后
仍被
bayes稳定压制 4.3–5.0%(17–18/20 场景,符号检验 p=0.0001),且 94% 的差距来自 多走的移动;其评估集 20 个场景全部是单源全向,无法支撑任何第四问结论。 - 不引入新的第三方运行时依赖(仍是
requests;numpy仅作可选加速后端,缺失时回退纯 Python)。
0.4 实施进展与实测回填(2026-09-12)
冻结基准集 harness/suites.py(第三问 18 主场景 + 2 边界;第四问 9 + 2),
并行 runner scripts/run_suite.py,配对口径同场景同误差种子。
| 项 | 状态 | 实测 | 结论 |
|---|---|---|---|
| P1-4 安全阀 | ✅ 已实现 | summary 新增 completion_basis / absence_evidence / redundant_rescan_measures / virtual_reserve_s;--persistent 输出单轮上界告警 |
采纳(5 项测试) |
| P0-2 最小命中集 | ✅ 已实现并默认开启 | 第四问 11 场景配对:14003.0 → 10975.5 s(均值 −19.3%、中位 −25.4%、10/11 更快);检测 444.5→319.2;请求 569.5→395.6;位移 54.9→44.2 km;最大 17007→12919 s;11/11 valid | 采纳 |
| P0-4 感知层向量化 | ✅ 已实现 | 单局计算 3.10 → 1.00 s(3.08×),最大 5.84 → 1.30 s;20 场景的虚拟时间与动作序列逐位一致 | 采纳(目标 ≤0.8 s 未达,余下为 belief 表示层) |
| P0-1 融合巡回 | ❌ 实测无效,已回退默认 | 仅追加站点方位线:3454.6 vs 3426.9 s(+0.8%,检测 +10.8 次、位移仅 −188 m);追加 + 抑制专用探点:4989.0 vs 3426.9 s(+45.6%,20/20 更慢) | 关闭(保留开关与代码) |
| P1-1 C₂₀ 最近清除点 | ⊘ 复核后降为非项 | 现有 _near_clear_target 已取安全球 B(c, limit−ρ) 内离当前位置最远点;与完整 C₂₀ 最优之差 ≤ slack+margin ≈ 0.74 m |
不改 |
| P1-3 基准集/并行 | ✅ 已实现 | 20 场景 × 3 变体 = 60 局 26.5 s(6 进程);20 局 3.9 s | 采纳 |
| P0-3 联合信念 | ✅ 已实施并于 2026-09-12 修正 | 网格粒子 × 24 档朝向 bin + 可见性选点;发现并修复 no_signal 朝向规则反向缺陷(原会删掉真源朝向档);配套 D1 探点口径使第四问后备盲清 125.7 → 4.3 次/局 |
采纳(结论与实测见图论与 DP 优化方案 §8) |
P0-1 为什么失败(重要负面结论):七站环上通常只有 2–3 个站能对同一源取得
有效方位线,且站间交会角不足以直接换到 20 m 证书,专用信息增益探点仍然必要;
"顺路测量"于是退化为平白多花检测,一旦抑制专用探点则会白等巡回、远端绕行。
结论:第三问的定位往返不是可回收的浪费,bayes 的滚动时域调度已在做正确的事。
第四问现状与目标差距(2026-09-12 图论/DP 轮回填):启用硬化覆盖计划
q4_mincover_v2 与 D1 探点口径后,第四问 11 场景(core + boundary)均值为
9069.9 s、最大 12081.1 s、后备盲清 24.8 次/局(core 9 场景 8602.0 s /
4.3 次),仍远高于 4000 s 验收线。剩余成本主要是最小命中集巡回
(23.9 km ≈ 4777 s)与缺席频道检测,二者都是覆盖证明的固有代价;
本方案 §2.3 的"可压缩 23–32%"在本模拟器上只回收了约 1%(详见图论与 DP 方案 §8)。
1. 运行历史复核
1.1 数据资产清单(本次实际读取)
| 资产 | 内容 | 可用信息 |
|---|---|---|
session.jsonl(31 011 行) |
官方第三问演练 J2WH-2K7D-5ZXS-DNK6 的客户端日志 | 10 323 次 /measure、12 次 /clear、逐动作计时 |
practice-p3-*.jlog |
同一局的官方行为日志封装 | 仅明文信封(题号 3、队号、案例码);载荷 AES-256-GCM + RSA-OAEP,不可解 |
referee-data/sessions/*(6 局,5 局完整) |
本地评测机 q3×3、q4×2、截断×1 | 逐请求响应、真值、summary、step 事件 |
output/hrl/ui/q3-main/ |
HRL 训练(109 776 步)+ eval/at-40k、eval/at-final |
monitor CSV、模型元数据、20 场景配对评估 |
docs/算法审核与优化-20260911.md、docs/algo-3.md、docs/algo-3-bayes.md |
历史 KPI 与消融 | 官方/自建成绩、验证集分档 |
| 本次新增复跑 | scripts/benchmark_q3_bayes.py(6 场景)、Q3/Q4 单局计时分解、cProfile、站点三角化可行性 |
§1.3 中标注"本次复跑"的数字 |
| 本次新增工具(已入库) | scripts/analyze_run_timing.py(任意会话日志的计时分相复算)、scripts/verify_q4_cover.py(第四问覆盖证书 + 最小命中集搜索) |
F4、F9 可复现 |
1.2 先确认度量可信(否则一切优化都是自欺)
官方计时模型:T = L/5 + 5·N_measure + N_switch + 5·N_clear⁺ + 3·N_clear⁻,
其中 L 为累计直线位移,/clear 不计换频。scripts/verify_q3_practice.py 对 5 局完整
评测机日志逐微秒复算,与服务器返回的 virtual_time_s 完全一致(例:BBM9 复算 3134.642907 = 记录 3134.642907)。
结论:后续所有"虚拟时间"结论都可用动作日志独立复算,这是本项目最可靠的一条工程资产,必须保留。
1.3 关键事实(F1–F13)
| 编号 | 事实 | 出处 |
|---|---|---|
| F1 | 虚拟时间构成:移动 75–83%|检测动作 15–21%|换频 2.3–3.7% | 官方会话 82.30/14.76/2.94;referee q3 80.50/16.78/2.71、q4 78.39/18.63/2.98;本次复跑 Q3 79.9/17.3/2.9、Q4 75.2/21.1/3.7 |
| F2 | 官方第三问演练 J2WH 总虚拟 350 227.7 s,其中 346 169.9 s(98.84%)是空复扫:7 个固定站点 × 8 个未清除频道 × 183 轮,0 检出,结束点正好落在 virtual_reserve_s=10 000 边界 |
session.jsonl 相位切分;planner/__init__.py:236、config.py:99 |
| F3 | 第四问的全部失败来自定向源:referee 两局全向 17/17 清除,定向仅 2/6;4/4 漏检全是定向;每清除源 356.4 s(Q3 274.0 s,+30%) | referee-data/sessions/{6X58,BAU8};{BBM9,RM7Y,VQKB} |
| F4 | 本次复跑 Q3(13 源,seed 0):3535.9 s / 109 次检测 / 13 清除;七站巡回 6960 m(1392 s,39%)+ 探点往返 5741 m(1148 s)+ 清除移动 3668 m(734 s) | 本次复跑(方法见附录 A) |
| F4b | 评测机 Q3 单局 BBM9:21 次专用探点占 1138.7 s 移动,等于该局虚拟时间的 36.3%,而站点扫频 98 次只占 991.2 s | scripts/analyze_run_timing.py referee-data/sessions/BBM9-.../log.jsonl --plan seven |
| F5 | 本次复跑 Q4(10 源 3 定向,seed 0):13 652.1 s / 560 次检测 / 18 次清除(8 次失败)/ 51 356 m | 本次复跑 |
| F6 | 感知层是墙钟热点:单局计算 6.04 s;cProfile 下 expected_survival 占累计时间 72%(2808 次调用),apply_measure ≈ 20%,_kill_outside_wedge 触发 498 万次 angular_gap |
本次 cProfile(13 源单局) |
| F7 | HRL 运行:109 776/200 000 步、4007.9 s、27.0 steps/s(6.75/env)、1.51 s 墙钟/局;被 SIGTERM 截断时回报仍在上升(Q1 1.05 → Q4 6.20) | output/hrl/ui/q3-main/q3.json + 4 份 monitor CSV |
| F8 | HRL 评估:20 场景全部单源全向;bayes 比 HRL 快 4.3–5.0%(17–18/20,p=0.0001),94% 的差距是多走的移动;40k→109k 步仅再改善 0.7% |
`eval/at-40k |
| F9 | 现役 Q4 发现集:700 m 方格 49 点、NN 巡回 36.4 km;满足同一"前向半盘命中"保证的贪心最小集 31 点、22.9 km(−37%),覆盖校核 100% | scripts/verify_q4_cover.py;本文附录 A |
| F10 | Q3 七站巡回已经最优:6 × 1159.7 = 6958 m(观测 6960 m),无可压缩空间;把环半径降到覆盖下界 1122.9 m 仅省 222 m(44 s) | 几何计算 + 本次复跑 |
| F11 | 站点三角化可行:99.28% 的源位置有 ≥2 个站在 1500 m 内;最佳交会角 5 分位 58.5°、中位 122.9°;只有 0.72% 的源位置仅 1 个站在射程内 | 本次离线采样(20 000 点) |
| F12 | 墙钟量级差三个数量级:内存回放 0.22–0.25 ms/请求(≈0.35 s/局),官方接口 9.42 ms 均值/请求(104.49 s / 10 337 请求);本地评测机 24.5–97.9 ms/请求 | referee session_end、runs/*/summary.json、session.jsonl elapsed_s |
| F13 | 历史 KPI 参照:Q3 官方最好 4058.71 s(3BW5,13/13)、5232.13 s(J4ER,12/12);自建 bayes 3707.3 s(本次 6 场景)/ 3726.2 s(14 场景消融)/ 验证集 3315.89 s;Q4 自建 13 164.7 s,无官方第四问演练记录 |
docs/算法审核与优化-20260911.md、docs/algo-3-bayes.md、README.md:135 |
1.4 由事实导出的六处结构性瓶颈
| 编号 | 瓶颈 | 支撑事实 | 归属层 |
|---|---|---|---|
| B1 | 定位/清除移动没有并入覆盖巡回,探点按"信息增益"选,移动权重 λ=0.0105 几乎不惩罚绕行 | F1、F4、F4b、F11 | 决策层 |
| B2 | Q4 发现集冗余 63%(49 点 vs 30 点),且每个"真缺席"频道要在全部 49 点各测一次 | F5、F9 | 保证层 |
| B3 | Q4 定向源缺少位置×朝向联合信念:no_signal 只记"被遮挡过几次",选点靠固定 45° 阶梯,收敛失败后退化到 ≤400 格 20 m 盲清(≤1200 s + ≤400 请求) |
F3、planner/__init__.py:691-768 |
感知层 + 决策层 |
| B4 | 感知层全 Python 容器:每局 20 个频道各建 ~6.1 万格 set,_kill_outside_wedge 每次遍历全部幸存格,评分核 expected_survival 为 7×800 的纯 Python 双循环 |
F6 | 感知层 |
| B5 | 研发吞吐低:场景串行、单进程;一局 0.35–6.5 s;HRL 27 steps/s;14–52 场景的配对基准要跑几分钟到十几分钟 | F6、F7、F12 | 实验层 |
| B6 | 运行治理缺失:单源评估集使指标退化(avg_locate_clear_time_s ≡ virtual_time_s)、eval/manifest.json 三份重复且与 output/hrl/ui/manifest.json 不一致(后者缺失)、persistent_search=True 可造成 98.84% 的无效开销且无硬性告警 |
F2、F8、F13 | 遥测/实验层 |
2. 目标架构
2.1 分层与依赖方向(依赖只能向下)
┌───────────────────────────────────────────────┐
L6 实验/遥测 │ bench/ 冻结基准集·并行 runner·配对统计 │
│ telemetry/ manifest·step log·replay·summary │
└───────────────▲───────────────────────────────┘
│ 只读观测
┌───────────────┴───────────────────────────────┐
L5 执行/安全 │ exec/ 动作账本·预算守卫·安全阀·幂等客户端 │
└───────────────▲───────────────────────────────┘
│ Action 请求
┌───────────────┴───────────────────────────────┐
L4 决策 │ plan/ 任务表·滚动时域排序·信息/时间评分 │
└───────────────▲───────────────────────────────┘
│ Task[]
┌───────────────┴───────────────────────────────┐
L3 保证 │ guarantee/ 命中集覆盖计划·清除证书·缺席证明 │
└───────────────▲───────────────────────────────┘
│ 候选集/幸存集
┌───────────────┴───────────────────────────────┐
L2 信念 │ belief/ BeliefBackend:OccupancyTensor(Q3) │
│ JointTensor(位置×朝向, Q4) │
└───────────────▲───────────────────────────────┘
│ Observation
┌───────────────┴───────────────────────────────┐
L1 观测模型 │ model/ 可见性半平面·r_eff 带·有界误差·舍入裕量 │
└───────────────▲───────────────────────────────┘
│ 原始响应
┌───────────────┴───────────────────────────────┐
L0 协议/时钟 │ protocol/ 规则表·计时模型·状态机·客户端(冻结)│
└───────────────────────────────────────────────┘
与现状的差别:用组合取代继承。现状是
OmnidirectionalLoop → SevenBatch → BayesLoop → DirectionalExtension 的单继承链,
调度、信念、清除、预算、报表五种职责混在同一族类里(planner/__init__.py 818 行 +
planner/bayes_loop.py 513 行),任何一处改动都会跨层影响其它三问。
目标形态是
Episode(
client = SimulatorClient(...), # L0,冻结
belief = OccupancyTensor(...) | JointTensor(...), # L2
plan = CoveragePlan.points, # L3
scorer = InfoGainPerSecond(...), # L4
policy = FusedTourPolicy(...) | DirectionalPolicy(...), # L4
certifier= Certificate(...), # L3
ledger = Telemetry(...), # L6
)
三层保证策略矩阵只差三个可替换部件,其余代码共享:
| 问题 | 信念后端 | 覆盖计划 | 选点策略 |
|---|---|---|---|
| Q3 | OccupancyTensor(全向) |
七站环(已经最优,冻结) | 融合巡回 |
| Q4 | JointTensor(位置×朝向) |
最小命中集(30 点,离线搜索 + 运行期校验证书) | 可见性约束选点 |
2.2 接口契约(关键签名)
# L1 model/observation.py —— 只此一处定义"什么算看见"
@dataclass(frozen=True)
class ObservationModel:
r_eff_min_m: float; r_eff_max_m: float
near_radius_m: float; half_angle_deg: float
directional_half_deg: float = 90.0 # 含边界
def visible(self, src: Vec2, heading_deg: float | None, reader: Vec2) -> bool: ...
def detectable(self, src: Vec2, heading_deg: float | None, reader: Vec2) -> bool: ...
# L2 belief/base.py —— 两种后端实现同一协议
class BeliefBackend(Protocol):
def apply(self, channel: int, reader: Vec2, result: str,
svd_deg: float | None) -> None: ...
def alive(self, channel: int) -> int: ...
def is_absent(self, channel: int) -> bool: ... # 幸存集为空
def certificate(self, channel: int) -> ClearCertificate | None: ...
def guaranteed_no_signal(self, channel: int, reader: Vec2) -> bool: ...
def prob_detect(self, channel: int, reader: Vec2) -> float: ... # 决策用
def version(self, channel: int) -> int: ... # 派生量缓存键
# L3 guarantee/cover.py —— 覆盖计划是"可离线搜索、可在运行期校验"的一等对象
@dataclass(frozen=True)
class CoveragePlan:
problem: int # 3 | 4
points: tuple[Vec2, ...]
certificate: CoverCertificate # 采样分辨率、缺失数、最坏裕量、脚本哈希
version: str
def verify(self, model: ObservationModel, tol_m: float) -> CoverCertificate: ...
# L4 plan/policy.py
class Policy(Protocol):
def tasks(self, belief: BeliefBackend, state: RobotState) -> list[Task]: ...
def order(self, tasks: list[Task], state: RobotState) -> list[Task]: ...
def measured_at_point(self, point: Vec2) -> tuple[int, ...]: ... # 一停多测
2.3 四条设计原则
- 保守性不变量不动:真源永不被删;清除证书始终基于完整 ±1.005° 角域与题面距离带; 所有启发式(均匀先验、朝向先验、信息增益)只用于选点,不用于安全证明。 Q4 的朝向联合信念必须自带"朝向未观测时退化为全向"的保守分支。
- 保证 = 离线搜索 + 运行期校验:覆盖集、清除点、巡回顺序都可以离线用数值搜索优化,
但运行期必须用同一份
ObservationModel重新校验证书(点集、分辨率、裕量), 校验失败即拒绝启动,而不是相信一个写死的点表。 - 一个事实一个来源:题面常量、
r_eff带、清除半径、动作耗时只在config.py; 可见性语义只在model/observation.py;训练/部署若将来共存,必须共用同一控制器模块 (这是旧 HRL 用 13 000 行换来的教训:v1 的 5 个特征索引错位、BC 激活函数不一致、 旧基准把completed当全清)。 - 每个优化都必须可配对、可复算、可回归:冻结基准集 + 逐局
manifest+ 动作日志- 逐微秒复算;任何"更快"的声明必须给出配对分布(均值/中位/最大/更快场数), 而不是单个最优场景。
2.4 迁移映射(现状 → 目标)
| 现状 | 目标 | 处理 |
|---|---|---|
protocol/*、evaluation/recompute_virtual_time |
不变 | 冻结,作为度量基准 |
belief/__init__.py(凸外包) |
belief/convex.py |
原样搬迁,继续负责精确角域证书 |
particles.py(网格幸存集) |
belief/occupancy.py(numpy 张量 + 纯 Python 回退) |
重写热核,语义不变,保留 tests/test_particles.py 的真源保留性质测试 |
| — | belief/joint.py(位置×朝向) |
新增,Q4 专用 |
coverage/ |
guarantee/cover.py |
49 点常量表 → 可校验的 CoveragePlan |
planner/bayes_loop.py 的 _build_tasks/_order_tasks |
plan/tasks.py、plan/schedule.py |
抽出,Q3/Q4 共用 |
planner/bayes_loop.py::_select_point |
plan/score.py |
抽出 + 向量化评分 |
planner/__init__.py::DirectionalExtension |
plan/policy.py::DirectionalPolicy + belief/joint.py |
保留后备清除作为最终兜底,但目标是不再触发 |
planner/seven.py |
guarantee/plans.py::SEVEN |
保留解析证明 |
| 4 个 benchmark 脚本 | bench/ 统一 runner(并行 + manifest + 配对统计) |
合并,消除指标口径分叉 |
3. 优化项
P0-1 第三问:发现—定位融合巡回(消除探点往返)
现状:每次收到 direction 后进入独立定位子循环,由信息增益选点器在同一站点之外
另选探点;13 源单局共 24 次探点、5741 m 往返、1148 s,与整个覆盖巡回(6960 m / 1392 s)同量级;
评测机单局 BBM9 更极端:21 次探点花掉 1138.7 s 移动 = 全局限时时间的 36.3%(F4b)。
评分函数 score = E[log 幸存占比] + 0.0105 × 移动秒数,移动项几乎不影响排名。
方案:把"已发现未定证"的频道加入每一个覆盖站点的批量扫频清单, 使第二/第三条方位线在巡回途中顺路取得;只有站点三角化不成立时才回退到专用探点。
- 巡回仍由
plan/schedule.py滚动排序(现有最近邻 + 2-opt 已足够,bayes的 −39.5% 移动 主要来自这里),新增"同点顺路测量"任务类型:Task(kind="station", channels=(未知, 未定证))。 - 站点任务的固有耗时按
6 s × 频道数 + 1 s × 换频次数记账(现有_path_cost已支持)。 - 保底:
F11显示 0.72% 的源位置只有 1 个站在 1500 m 内,对这类频道保留_select_point的专用探点路径;判定用belief.prob_detect而非硬编码。
预期收益(已被实测推翻):原估 F4 的 1148 s 探点移动可回收 70–80%, 即 −800 ~ −900 s。实测结果相反:仅追加站点方位线 +0.8%,追加并抑制专用探点 +45.6%(20/20 场景更慢),见 §0.4。原因是站间交会角不足以保证 20 m 证书, 专用信息增益探点仍然必要;抑制它只会白等巡回。本项不采纳,默认关闭。
验收:18 场景验证集平均 ≤ 2700 s、最大 ≤ 4000 s;measures 增幅 ≤ 20%;
move_distance_m 降幅 ≥ 25%;全部 valid。
风险:站点距离最远 2 × 1159.7 = 2319 m,可能出现 no_signal 浪费 5 s。
缓解:仅在 prob_detect > 0 时顺路测量;no_signal 同样是有效负观测(进信念,不算异常)。
P0-2 第四问:用最小命中集替换 49 点发现集(离线搜索 + 运行期校验)
现状:coverage_points() 生成 700 m 方格 49 点(±2100),依据是"单元内任一点到 4 顶点
≤ 700√2 ≈ 989.95 ≤ 1000"。该条件是充分但远非必要:它要求单元内任意位置、任意朝向都由
同一个顶点命中。实际上,第四问的保证是"对 ∀(p,h) ∈ Ω×S¹,存在被访点位于其前向半盘内",
允许由多个顶点共同围住 p。
方案:把它当作一个显式的击中集(hitting set)问题离线求解,运行期校验:
- 目标空间离散:
p取 Ω 内 75 m 网格,h取 24 个方向 → 43 032 个 (p,h) 单元; - 候选点:半径 2850 m 圆盘内 200–300 m 网格(641/293 个候选);
- 贪心集合覆盖 + 局部改进,同时把巡回长度作为次级目标(641/293 个候选中取 31/30 个, 仍有大量同覆盖度的等价解可用于缩短巡回);
- 生成
CoveragePlan(points, certificate),certificate记录采样分辨率、未覆盖数、 最坏裕量、生成脚本哈希;CoveragePlan.verify()在每次运行前用同一ObservationModel重新校核,未通过即拒绝启动。
本次离线校核结果(附录 A 可复现):
| 点集 | 点数 | (p,h) 覆盖 | NN 巡回 | 折算移动 |
|---|---|---|---|---|
| 现役 700 m 方格 ±2100 | 49 | 100% | 36.4 km | 7280 s |
| 三角格 1000 m(半径 2800 盘) | 31 | 100% | 34.3 km | 6863 s |
| 贪心最小命中集(200 m 候选) | 31 | 100% | 22.9 km | 4587 s |
| 贪心最小命中集(300 m 候选) | 30 | 100% | 23.6 km | 4719 s |
实测收益(2026-09-12,11 场景配对):均值 14003.0 → 10975.5 s(−19.3%), 中位 −25.4%,10/11 场景更快;检测 444.5 → 319.2,请求 569.5 → 395.6, 位移 54.9 → 44.2 km,后备清除尝试 113.3 → 64.1,11/11 valid。 低于原估的 −26%,差额来自"缺席频道省下的检测"被定位探点部分抵消。
验收:CoveragePlan.verify() 通过;10/13/16 源 × 定向 1/半数/仅一个全向 × 边界朝外压力
场景全部全清;请求数、日志体积仍满足 1200 s / 2 MB 预算。
风险:数值采样证书弱于解析证明。缓解:(a) 保留解析七站证明用于 Q3; (b) 证书采样分辨率与裕量写入 summary 并随日志归档;(c) 保留 10% 的额外"保险点"预算, 若演练观测到任何漏检立即回退到 49 点表。
P0-3 第四问:位置×朝向联合信念与可见性选点
现状:定向源只被当作"候选空间集合",no_signal 只用于累计 blocked_probes
(planner/__init__.py:663-677),选点沿固定 45° 阶梯试探;梯子用尽后调用
_fallback_clear,对候选外包多边形做 20 m 网格覆盖逐格 /clear(≤400 格、≤1200 s、≤400 请求)。
运行历史显示这正是第四问的失败点:定向源 2/6 清除,4/4 漏检全是定向。
方案:为第四问频道维护 (x, y, heading) 联合可行集,把"看不看得见"变成可推理的量:
- 表示:
JointTensor—— 复用 Q3 的占用网格,每格附加 24 个朝向 bin 的可行性位掩码 (uint8 位域,20 频道 × 6.1 万格 × 3 B ≈ 3.7 MB,可接受)。 - 更新规则(全部保守,仅删"必然看不见"的格):
direction@s:位置删>1500、角向删|gap| > δ_eff + 角裕量;朝向删 "以该格为源、朝向不覆盖 s" 的半盘(保留 ±90° 含边界)。no_signal@s:对|g−s| ≤ 1000的格,朝向只能落在背向 s 的半盘; 对1000 < |g−s| ≤ 1500的格,朝向要么背向 s,要么源实际半径 < |g−s|(保持不确定); 对|g−s| > 1500的格不删。near@s:位置收缩到 5 m 盘,朝向置为全向(近场无遮挡语义)。
- 决策:
prob_detect(channel, reader)= 该点能看到该频道任一可行 (p,h) 的概率质量; 选点直接最大化prob_detect / (移动秒数 + 6),即保证有信号的信息增益/秒。 - 定位收敛:位置由多条
direction的角域交会收缩(与 Q3 相同);朝向在 2 次 "一次被遮挡 + 一次可见"后通常已被压到很窄的楔形,此时可主动选择必然可见的探点, 用一个正常角域证书结束,而不再进入 400 格盲清。 - 后备清除保留(
fallback_clear_cells已有格数与预算双硬顶),但目标变为 "只在证书无法建立时触发",并在 summary 中把触发次数作为回归指标。
预期收益(未实测):定向频道的探点次数与后备清除触发次数大幅下降。参考量级:
后备清除上限 400 格 × 3 s = 1200 s + 400 请求/频道;docs 记录的 Q4 改进版每局后备
尝试 616/4 局 ≈ 154 次。保守估计 −1500 ~ −3000 s/局。
验收:10 源 3 定向场景全清率 100%(现状 referee 仅 2/6 定向);
fallback_clear_attempts 相对基线下降 ≥ 70%;定向信道平均检测次数 ≤ 全向信道的 1.5 倍。
风险:朝向均匀先验是工程假设,官方分布未知。缓解:先验只影响选点顺序,
不影响任何安全性判定;朝向不确定时自动退化为全向分支(prob_detect 取全向值)。
P0-4 感知层向量化与并行(研发运行速度)
现状(F6):expected_survival 占单局 CPU 的 72%,2808 次调用 × (7 代表点 × 800 子采样)
≈ 1570 万次纯 Python 距离/角度运算;apply_measure 另有 20%,其中 _kill_outside_wedge
每次遍历全部幸存格(498 万次 angular_gap)。
方案:
belief/occupancy.py用numpy布尔/整数张量替换set[int]: 每频道 248×248 uint8(≈61 KB),20 频道 ≈ 1.2 MB;盘/楔删除用切片 + 布尔掩码, 粗格索引用np.add.reduceat;numpy缺失时回退现有纯 Python 实现(测试保证两条路径同结果)。- 采样评分向量化:把
subsample与representatives预转(n,2)数组, 一次广播算完距离、角度、角向 gap,expected_survival由 5600 次标量循环变成 2–3 条向量指令。 - 站点级编译缓存:
(station, channel, version)→ 掩码结果,version不变直接复用; 当前_skip_cache只缓存"必 no_signal"布尔,可扩展到整块删除掩码。 - MEC 增量维护:当前
mec()每次版本变化重算全部幸存点(最多 6 万点);改为 版本变化时只对新增删除点做包围圆增量更新,或按 1/8 子采样给出候选、证书判定用精确值。 - 批量基准并行化:
bench/runner.py用concurrent.futures.ProcessPoolExecutor按场景分发(每进程独立LocalSimulator,真值仍只进测试端),Mac M 系列 8 核可得 ~6–8×。 - 观测/遥测:单局 summary 写入
compute_wall_s、profile_top(可选--profile), 纳入基准表的必填列,防止"虚拟时间变快、墙钟悄悄变慢"。
实测收益(2026-09-12):单局计算 3.10 → 1.00 s(3.08×),最坏 5.84 → 1.30 s;
20 场景基准集 12.2 → 3.9 s。虚拟时间与动作序列逐位一致(纯 Python 回退路径保留,
MCM26B_NO_NUMPY=1 可强制)。未达 ≤0.8 s 的目标:剩余开销在
apply_measure 的网格删除与 ChannelParticles.__init__,需要 P0-4b(表示层张量化)。
验收:tests/test_particles.py、tests/test_bayes_loop_q3.py 全过;
纯 Python 与 numpy 两条路径在同一动作日志下产出逐位相同的 summary(actions_sha256 相同);
单局 compute_wall_s ≤ 0.8 s。
P1-1 清除点选在 C₂₀(K) 内离当前位置最近处
现状 _near_clear_target 只沿"当前位置 → 候选圆心"方向平移,平移量受
limit − radius 限制,是被证明的一维特例。一般解是
C₂₀(K) = ∩_{g∈K} B(g, 20),取其内离当前位置最近的点:
- 对凸多边形候选集,
C₂₀(K)仍是凸集(若干圆盘之交),可用"到当前点的最近点"的 简单迭代/投影求得; - 若
C₂₀(K)非空,则从该点清除的移动代价最小,且三角不等式保证仍在 20 m 内。
预期收益:Q3 单局清除移动 3668 m(734 s)中回收 20–35%(−150 ~ −260 s);
Q4 同步受益。
风险:C₂₀ 空时退回现有 MEC 圆心逻辑(已实现)。
P1-2 覆盖几何参数改为"离线搜索 + 运行期校验"
- Q3:
station_ring_radius_m由 1159.7 的可证明下界 1122.9 重算,并在validate_seven_coverage中同时校验 PF 网格覆盖(现行文档已记录 1159.7 与 Optuna 建议值差 3.3 m 的取舍)。 收益很小(≈44 s),但把"设计性质 vs 1.7% 均值差异"的取舍写成可复算脚本,而不是口口相传。 - Q4:见 P0-2,覆盖集与巡回联合优化(在等价覆盖解中选巡回最短者, 预期可在 23.6 km 基础上再降 5–10%)。
- 所有覆盖计划产物落
output/<problem>/coverage/<version>.json+ 证书,纳入版本管理。
P1-3 评估与遥测重构(消除 B6)
- 冻结基准集(
bench/suites/,一题一份、版本化、写入manifest.json):- Q3:10/13/16 源 × 8 个种子 + 2 个圆周边界场景(
r_eff=1000,扇区接缝); - Q4:10 源 3 定向、13 源 6 定向、16 源 8 定向 × 每种 3 个种子 + 朝外边界压力场景;
- 场景与真值只进模拟器与校验器,策略端看不到(真值隔离不变量保持不变)。
- Q3:10/13/16 源 × 8 个种子 + 2 个圆周边界场景(
- 统一 runner + 逐局 artifact:
manifest.json/results.json/aggregate.{json,csv}/runs/<scene>/<variant>/summary.json+session.jsonl,任何一局无效则all_valid=false且非零退出。 - 指标口径去重:目前
avg_locate_clear_time_s ≡ virtual_time_s(单源评估集下二者必然相等), 必须在多源基准集上重新定义并标注:T_total(整场虚拟时间)、T_accounted(首次证明找全时刻)、T_last_clear、T_total / cleared、T_total / truth。 - 运行历史持久化:
output/目前不纳入 git,导致output/rl/review-20260911/、output/q3/optimization-20260911/等证据在 revert 后无法追溯。 本轮起把**基准集的 summary/aggregate/manifest(压缩,不含大日志)**纳入版本管理, 官方 jlog/日志仍按题面要求单独归档。
P1-4 安全阀与退出可解释性
| 阀门 | 现状 | 改进 |
|---|---|---|
persistent_search |
默认已关闭,但没有任何"这会浪费多少"的提示;历史上已造成一次 346 170 s(98.84%)的无效演练 | 显式开启时输出预估上限(未清除频道数 × 站点数 × 6 s)并要求二次确认;summary 记录 redundant_rescan_measures |
virtual_reserve_s |
10 000 s 固定 | 改为 max(10 000, 2 × 最远单动作上界),并在接近时把 stop_reason 与剩余预算写入 summary |
| 完成判定 | completed = 无中断 ∧ (清除数≥16 ∨ 每频道 CLEARED/ABSENT) |
增加 completion_basis ∈ {count_cap, per_channel_proof} 字段,避免把"数量上界"与"逐频道证明"混为一谈 |
| 缺席证明 | 覆盖账本 + PF 双通道 | summary 增加 absence_evidence[channel] ∈ {pf_empty, coverage, count_cap},供论文与审计逐条追溯 |
P2-1 选点评分的解析化/查表化
expected_survival 的物理含义可用解析近似替代:对给定测点 q 与候选集,
E[log 幸存占比] 的一阶近似只依赖 (候选集到 q 的距离分布, 角向展布)。
先在离线基准上验证解析近似与采样评分选出的点一致率 ≥ 90%,再切默认实现。
预计再省 30–50% 的评分开销(P0-4 之后已不是瓶颈,列入 P2)。
P2-2 是否重启学习类方法的门控条件
只有在下列四条同时满足时才考虑把 ML 重新引入,且只允许替换"任务排序"这一个部件:
- P0-1/P0-2/P0-3 全部落地并通过基准集验收;
- 手写基线仍有 ≥ 10% 的可测改进空间,且已定位到具体的排序子问题;
- 训练与部署共用同一控制器模块,并有逐状态一致性测试(旧 HRL 的 13 000 行里最有价值的
就是这条纪律:
HRLControllerMixin被 env 与 runtime 逐字节共用); - 评估集包含多源与定向场景(现役 20 场景单源评估集不足以支撑任何结论)。
4. 实施路线图
| 阶段 | 内容 | 交付物 | 验收门槛 | 依赖 |
|---|---|---|---|---|
| S1(P0,2–3 天) | P0-2 最小命中集 + P0-4 并行基准;P1-3 冻结基准集 + manifest;P1-4 安全阀 | guarantee/cover.py、bench/runner.py、bench/suites/*、docs/ 更新 |
Q4 巡回 ≤ 25 km;基准集并行跑完 ≤ 60 s;全部 valid |
无 |
| S2(P0,2–3 天) | P0-1 融合巡回(Q3);P1-1 清除点 | plan/schedule.py、plan/score.py、Episode 组合化重构 |
Q3 均值 ≤ 2700 s、最大 ≤ 4000 s;移动 −25% | S1 的 runner |
| S3(P0/P1,3–5 天) | P0-4 信念层 numpy 化;P0-3 联合信念 + 可见性选点(Q4) | belief/occupancy.py、belief/joint.py、plan/policy.py |
单局计算 ≤ 0.8 s;Q4 定向全清 100%、后备触发 −70% | S2 的接口 |
| S4(P1/P2,2–3 天) | P1-2 覆盖参数搜索固化;P2-1 评分解析化;论文数据表 | 覆盖计划证书、docs/paper 数据 |
与 S3 结果配对不退化 | S3 |
| S5(演练) | 官方演练:Q3 用全向、Q4 用混合;先实测单请求延迟与总请求数,核对 1200 s / 2 MB | 官方日志与报告 | 全清 + 正常退出 + 计时复算一致 | 团队排期 |
顺序约束:S1 的基准集必须先于任何算法改动落地,否则无法配对;否则"更快"不可证。
5. 验收与度量
5.1 有效局定义(沿用现有基准脚本,不得放宽)
valid := 全部真实源被清除
∧ 被判缺席的频道都与真值一致(无误判)
∧ 正常 /exit(exit_reason == user_exit)
∧ 零算法异常(anomalies == [])
∧ 逐动作虚拟时间复算与服务器返回逐微秒一致
5.2 必报指标(每个变体、每个场景,落盘 + 汇总)
- 虚拟时间:
T_total、T_accounted、T_last_clear、T_total/cleared,以及 均值/中位/最大/P90 与 < 4000 s 场次比例; - 动作:
measures、clears、failed_clears、fallback_clear_attempts、redundant_rescan_measures、total_requests; - 移动:
move_distance_m,以及 move/action/switch 三分项占比; - 墙钟:
compute_wall_s、请求吞吐(ms/请求)、批量基准总时长、训练 steps/s(若涉及); - 配对比较:同场景同误差种子下
1 − T_variant / T_baseline的均值、 更快场数、以及对场景做 10 000 次 bootstrap 的 95% 区间(参照summarize_rl_revision.py的做法)。
5.3 回归
uv run pytest(当前 232 项)必须全过;新增:
CoveragePlan.verify()的正/负例(故意删一个点必须被检出);- 联合信念的真源保留性质测试(随机 (p,h) × 随机观测序列,真源 (p,h) 永不被删);
- 融合巡回的"每源至少一条站点方位线"性质测试;
- 逐位一致性测试:numpy 后端与纯 Python 后端在同一动作序列下 summary 完全相同。
6. 风险与明确不承诺的部分
6.1 风险登记
| 风险 | 影响 | 缓解 |
|---|---|---|
| 最小命中集证书依赖数值采样,官方分布未知 | 极端 (p,h) 漏检 → 漏源 | 保留解析七站证明(Q3);Q4 证书采样分辨率/裕量写入 summary;保留 10% 保险点预算与 49 点回退开关 |
| 站点三角化在 0.72% 源位置上只有 1 站在射程内 | 这些场景仍需专用探点 | prob_detect 触发回退路径,保留现有 _select_point |
| 朝向先验为工程假设 | 选点次优 | 先验只影响顺序;不确定时退化为全向;安全性由 20 m 证书 + 覆盖证明负责 |
| numpy 化引入浮点路径差异 | 真源保留性质被破坏 | 两条后端同时保留并对拍;保持整数微秒复算计时 |
| 并行基准引入进程/端口/临时目录问题 | 基准不稳定 | 每进程独立内存回环;不共用端口;manifest 记录随机种子 |
6.2 不承诺的部分
- 第四问 4000 s:10 源场景下,仅"发现 + 缺席证明"就约需
最小命中集巡回 4719 s + 缺席检测 ≈1900 s ≈ 6600 s,加之定位清除,4000 s 不可达。
若必须逼近该线,需要改变停止条件(例如接受
unresolved_channels而非逐频道缺席证明), 那是成绩口径的取舍,必须先与题面评分方式核对,本轮不作为代码默认行为。 - 本文所有收益数字都是自建模拟器上的估计或本次复跑观测,不是官方成绩; 官方分布、r_eff 分布、误差场分布仍是测试假设。
- 不声称 P0-1/P0-3 已在官方演练验证——需新的演练记录。
附录 A 复现命令与本次新增实验
# 1) 基线配对(本次复跑:bayes 3707.3 s vs seven_batch 5581.8 s,局部 −32.1%)
uv run python scripts/benchmark_q3_bayes.py --seeds 0,1 --out /tmp/mcmopt/q3
# 2) 运行历史计时分解(已入库):任意会话日志 → 移动/检测/换频分相 + 重复复扫检测
uv run python scripts/analyze_run_timing.py session.jsonl --plan seven
uv run python scripts/analyze_run_timing.py referee-data/sessions/BBM9-HZG2-77N5-NKKC/log.jsonl --plan seven
uv run python scripts/analyze_run_timing.py referee-data/sessions/6X58-XQ7U-CJ24-JQQR/log.jsonl --plan 49
# 实测输出(与服务器记录最大偏差 0.000000 s,证明复算口径正确):
# session.jsonl 350227.692 s,移动 82.3%,检测 10323 次,重复率 96.4,
# 末次清除后仍花 346169.9 s / 10225 次空复扫
# BBM9 (Q3) 3134.643 s,移动 75.9%,探点 21 次 / 1138.7 s
# 6X58 (Q4) 3251.021 s,移动 78.3%,检测 112 次
# 3) 第四问覆盖证书与最小命中集(已入库)
uv run python scripts/verify_q4_cover.py --out output/q4/cover-certificate.json
# 实测输出:目标空间 43032 个 (p,h)
# 现役 49 点方格:缺口 0,NN 巡回 36.4 km(7280 s 移动)
# 贪心最小命中集:31 点,缺口 0,NN 巡回 22.9 km(4587 s 移动)
# 4) 感知层剖面(单局 6.04 s 计算中 expected_survival 占 ~72%)
python -m cProfile -s cumulative <单局驱动脚本>
第 2、3 项已作为常驻工具入库,使"虚拟时间花在哪"与"覆盖集是否仍然合法" 成为可随时重跑的回归项;第 1、4 项仍是一次性分析,建议在 S1 阶段并入
bench/runner.py的必报指标(compute_wall_s、profile_top)。
附录 B 数字出处索引
| 数字 | 出处 |
|---|---|
| 移动 80.50% / 16.78% / 2.71%(Q3) | referee-data/sessions/{BBM9,RM7Y,VQKB}/log.jsonl 按 verify_q3_practice.py 模型复算 |
| 移动 82.30%(官方 J2WH) | session.jsonl 求和 |
| 346 169.9 s 空复扫 | session.jsonl /exit 时刻 350 227.691846 s 减去 12 源首轮清除的 4 057.839 s |
| Q4 定向 2/6、漏检 4/4 全为定向 | referee-data/sessions/{6X58,BAU8}/meta.json + log.jsonl |
| bayes 3707.3 / seven 5581.8 | 本次复跑 benchmark_q3_bayes.py --seeds 0,1 |
| 3535.9 s / 109 检测 / 6960 m 巡回 / 5741 m 探点 | 本次复跑计时分解 |
| 13652.1 s / 560 检测 / 51 356 m(Q4) | 本次复跑单局 |
expected_survival 72% |
本次 cProfile 单局 |
| HRL 27.0 steps/s、4007.9 s、109 776 步 | output/hrl/ui/q3-main/q3.json + *.monitor.csv |
| bayes 比 HRL 快 4.3–5.0%、94% 差距为移动 | output/hrl/ui/q3-main/eval/at-final/aggregate.json + runs/*/summary.json |
| 站点三角化 99.28%、交会角 5 分位 58.5° | 本次离线采样 20 000 点 |
| 49 点 vs 30 点覆盖集 | 本次贪心击中集计算(附录 A 第 5 项) |