yes fuking shit

This commit is contained in:
Horldsence
2026-09-12 14:48:43 +08:00
parent 61fd22598a
commit 45e48d4c91
64 changed files with 11540 additions and 57 deletions
+91
View File
@@ -0,0 +1,91 @@
"""P1-4 安全阀与退出可解释性验收。
覆盖 docs/第三四问架构优化与运行提速方案.md §P1-4:
- summary 必须显式给出完成依据 completion_basis(数量上界 vs 逐频道证明);
- 逐频道缺席必须带证据来源 absence_evidence(pf_empty / coverage);
- 重复复扫计数 redundant_rescan_measures 是历史 98.84% 空复扫问题的回归指标;
- 虚拟安全余量不小于单动作耗时上界(配置被调小时仍成立)。
"""
import unittest
from dataclasses import replace
from mcm26b.config import ProblemConfig
from mcm26b.planner import DirectionalExtension, OmnidirectionalLoop
from mcm26b.planner.bayes_loop import BayesLoop
from mcm26b.protocol.client import SimulatorClient
from mcm26b.harness import LocalSimTransport, LocalSimulator
from mcm26b.harness import mixed_scene, random_omni_scene
CONFIG = replace(ProblemConfig(),
search=replace(ProblemConfig().search,
bearing_rounding_margin_deg=0.005,
persistent_search=False))
def _run(loop_cls, sources, config=CONFIG):
simulator = LocalSimulator(sources, robot_id="T-SAFE", config=config)
client = SimulatorClient("T-SAFE", config=config,
transport=LocalSimTransport(simulator))
return loop_cls(client, config).run(), simulator
class SafetyValveTests(unittest.TestCase):
def test_summary_completion_and_absence_evidence(self):
sources = random_omni_scene(seed=5, count=10, config=CONFIG)
summary, _ = _run(BayesLoop, sources)
truth = sorted(s.channel for s in sources)
self.assertEqual(sorted(summary["cleared_channels"]), truth)
self.assertEqual(summary["completion_basis"], "per_channel_proof")
self.assertEqual(sorted(summary["absent_channels"]),
sorted(set(range(1, 21)) - set(truth)))
evidence = summary["absence_evidence"]
self.assertEqual(sorted(int(c) for c in evidence),
sorted(summary["absent_channels"]))
self.assertTrue(set(evidence.values()) <= {"pf_empty", "coverage"})
self.assertIsInstance(summary["redundant_rescan_measures"], int)
self.assertGreaterEqual(summary["virtual_reserve_s"], 10_000.0)
def test_redundant_rescan_measure_is_counted(self):
"""同一测点重复检测必须被计数(正常模式应为 0)。"""
sources = random_omni_scene(seed=7, count=10, config=CONFIG)
summary, _ = _run(BayesLoop, sources)
self.assertEqual(summary["redundant_rescan_measures"], 0)
def test_count_cap_is_distinguished_from_proof(self):
"""16 源时按数量上界停止,completion_basis 不得写成逐频道证明。"""
sources = random_omni_scene(seed=11, count=16, config=CONFIG)
summary, _ = _run(BayesLoop, sources)
self.assertGreaterEqual(len(summary["cleared_channels"]), 16)
self.assertEqual(summary["completion_basis"], "count_cap")
def test_q4_completion_basis_present(self):
sources = mixed_scene(seed=3, count=10, directional=3, config=CONFIG)
summary, simulator = _run(DirectionalExtension, sources)
truth = sorted(s.channel for s in sources)
self.assertEqual(sorted(summary["cleared_channels"]), truth)
self.assertIn(summary["completion_basis"],
{"per_channel_proof", "count_cap"})
self.assertEqual(sorted(simulator.cleared), truth)
class VirtualReserveTests(unittest.TestCase):
def test_reserve_covers_single_action_bound(self):
"""配置余量被调小到 0 时,有效余量仍不得小于单动作上界。"""
tight = replace(ProblemConfig(),
search=replace(ProblemConfig().search,
virtual_reserve_s=0.0))
sources = random_omni_scene(seed=1, count=1, config=tight)
simulator = LocalSimulator(sources, robot_id="T-RES", config=tight)
client = SimulatorClient("T-RES", config=tight,
transport=LocalSimTransport(simulator))
loop = OmnidirectionalLoop(client, tight)
reach = max((x * x + y * y) ** 0.5 for x, y in loop.ledger.points)
bound = 2.0 * reach / tight.cost.speed_mps \
+ tight.cost.measure_s + tight.cost.switch_s
self.assertGreaterEqual(loop._min_virtual_reserve(), bound)
self.assertLess(loop._min_virtual_reserve(), 10_000.0)
if __name__ == "__main__":
unittest.main()