100 lines
4.6 KiB
Python
100 lines
4.6 KiB
Python
"""覆盖计划(CoveragePlan)与运行期校核验收(方案 §P0-2)。
|
||
|
||
要点:
|
||
- 计划是"可离线搜索、可在运行期校验"的一等对象,运行期必须重新校核;
|
||
- 校核必须能**拒绝**非法点集(删点即被检出),否则校核是装饰;
|
||
- 两类覆盖语义不得混用:第三问 full_disk、第四问 forward_half_disk;
|
||
- 第四问最小命中集必须在同一采样空间上与原 49 点表等价覆盖。
|
||
"""
|
||
|
||
import unittest
|
||
|
||
from mcm26b.config import ProblemConfig
|
||
from mcm26b.coverage import coverage_points
|
||
from mcm26b.guarantee import (plan_instance, plan_names, seven_plan,
|
||
square_49_plan, verify_full_disk,
|
||
verify_forward_half_disk, verify_plan)
|
||
|
||
|
||
class CoveragePlanTests(unittest.TestCase):
|
||
def test_q4_mincover_plan_is_certified(self):
|
||
self.assertIn("q4_mincover_v1", plan_names())
|
||
plan = plan_instance("q4_mincover_v1")
|
||
self.assertEqual(plan.problem, 4)
|
||
self.assertEqual(plan.coverage, "forward_half_disk")
|
||
certificate = verify_plan(plan)
|
||
self.assertEqual(certificate.uncovered, 0, certificate.as_dict())
|
||
self.assertTrue(certificate.valid)
|
||
# 最小命中集必须显著小于旧的充分条件表
|
||
self.assertLess(len(plan.points), len(square_49_plan().points))
|
||
self.assertLess(certificate.nn_tour_m, 25_000.0)
|
||
|
||
def test_hardened_plan_passes_fine_grid(self):
|
||
"""S2:现役计划必须在 15 m/72 档(325.6 万个 (p,h))零缺口。
|
||
|
||
75 m/24 档的"零缺口"曾是采样过拟合(v1 在 25 m/48 档有 125 个缺口)。
|
||
"""
|
||
self.assertIn("q4_mincover_v2", plan_names())
|
||
plan = plan_instance("q4_mincover_v2")
|
||
certificate = plan.certificate
|
||
self.assertIsNotNone(certificate)
|
||
self.assertEqual(certificate.target_step_m, 15.0)
|
||
self.assertEqual(certificate.headings, 72)
|
||
verified = verify_plan(plan)
|
||
self.assertEqual(verified.uncovered, 0, verified.as_dict())
|
||
self.assertLess(verified.worst_hit_distance_m, 1000.0)
|
||
self.assertTrue(verified.valid)
|
||
|
||
def test_legacy_overfit_plan_fails_fine_grid(self):
|
||
"""负例:旧的 31 点表在细网格上有缺口,硬化不是装饰。"""
|
||
legacy = plan_instance("q4_mincover_v1", verify=False)
|
||
certificate = verify_forward_half_disk(list(legacy.points), 25, 48)
|
||
self.assertGreater(certificate.uncovered, 0)
|
||
|
||
def test_square49_and_mincover_cover_the_same_space(self):
|
||
for name in ("square49", "q4_mincover_v1"):
|
||
certificate = verify_forward_half_disk(plan_instance(name).points)
|
||
self.assertEqual(certificate.uncovered, 0, name)
|
||
|
||
def test_verifier_rejects_a_broken_plan(self):
|
||
"""删掉关键点必须被检出——否则运行期校核没有意义。"""
|
||
points = list(plan_instance("q4_mincover_v1").points)
|
||
broken = points[:-1]
|
||
certificate = verify_forward_half_disk(broken)
|
||
self.assertGreater(certificate.uncovered, 0)
|
||
self.assertFalse(certificate.valid)
|
||
|
||
def test_hardened_plan_rejects_a_removed_point(self):
|
||
"""硬化计划同样必须能拒绝删点(在证书分辨率上复核)。"""
|
||
plan = plan_instance("q4_mincover_v2")
|
||
points = list(plan.points)
|
||
certificate = verify_forward_half_disk(
|
||
points[:-1], plan.certificate.target_step_m,
|
||
plan.certificate.headings)
|
||
self.assertGreater(certificate.uncovered, 0)
|
||
|
||
def test_seven_plan_full_disk_semantics(self):
|
||
plan = seven_plan(1200.0)
|
||
certificate = verify_full_disk(plan.points)
|
||
self.assertEqual(certificate.kind, "full_disk")
|
||
self.assertEqual(certificate.uncovered, 0)
|
||
|
||
def test_seven_plan_too_small_ring_is_rejected(self):
|
||
"""环半径过小则 Ω 边缘覆盖不全,full_disk 校核必须报缺口。"""
|
||
certificate = verify_full_disk(seven_plan(800.0).points)
|
||
self.assertGreater(certificate.uncovered, 0)
|
||
|
||
def test_square49_order_matches_legacy_ledger(self):
|
||
"""历史基线 grid49_immediate 必须逐位可复现(点集与顺序都一致)。"""
|
||
self.assertEqual(list(square_49_plan().points),
|
||
list(coverage_points(ProblemConfig())))
|
||
|
||
def test_coverage_semantics_are_not_interchangeable(self):
|
||
"""第三问七站点集不满足第四问"前向半盘命中"语义(朝向遮挡)。"""
|
||
certificate = verify_forward_half_disk(seven_plan(1200.0).points)
|
||
self.assertGreater(certificate.uncovered, 0)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|