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
+99
View File
@@ -0,0 +1,99 @@
"""覆盖计划(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()
+124
View File
@@ -0,0 +1,124 @@
"""第四问 (位置 × 朝向) 联合信念验收(docs/第三四问图论与动态规划优化方案.md §3.1)。
要点:
- 真源 (位置, 朝向) 在与真值一致的观测序列后**永不**被删(保守性二维推广);
- 期望后验 MEC 半径是合法的选点值函数:远处被朝向遮挡的 no_signal 不删
**空间**候选,因此不应被误当成"已经收缩";
- numpy 向量路径与纯 Python 回退路径在同一输入下结果一致。
"""
import math
import unittest
from dataclasses import replace
from random import Random
from mcm26b.belief.joint import JointBelief, _np
from mcm26b.config import ProblemConfig
from mcm26b.protocol.rules import DIRECTION, NEAR, NO_SIGNAL
CONFIG = replace(ProblemConfig(),
search=replace(ProblemConfig().search,
bearing_rounding_margin_deg=0.005))
HALF_ANGLE = CONFIG.field.directional_half_angle_deg
def _visible(source, heading, reader, limit_m):
if math.dist(reader, source) > limit_m:
return False
bearing = math.degrees(math.atan2(reader[1] - source[1],
reader[0] - source[0]))
return abs((bearing - heading + 180.0) % 360.0 - 180.0) <= HALF_ANGLE - 5.0
class JointBeliefRetentionTests(unittest.TestCase):
def test_true_source_survives_consistent_observations(self):
"""与真值一致的观测序列后,真源所在格仍必须存在。"""
rng = Random(11)
checked = 0
while checked < 10:
source = (rng.uniform(-1700, 1700), rng.uniform(-1700, 1700))
if math.hypot(*source) > 1700:
continue
heading = rng.uniform(0.0, 360.0)
detection = (rng.uniform(-2100, 2100), rng.uniform(-2100, 2100))
if not _visible(source, heading, detection,
CONFIG.field.r_eff_min_m):
continue
checked += 1
joint = JointBelief(CONFIG)
bearing = math.degrees(math.atan2(source[1] - detection[1],
source[0] - detection[0])) % 360.0
joint.apply(detection, DIRECTION,
round(bearing + rng.uniform(-0.9, 0.9), 2) % 360.0)
for _ in range(5):
probe = (rng.uniform(-1800, 1800), rng.uniform(-1800, 1800))
if _visible(source, heading, probe, CONFIG.field.r_eff_min_m):
pb = math.degrees(math.atan2(source[1] - probe[1],
source[0] - probe[0])) % 360.0
joint.apply(probe, DIRECTION,
round(pb + rng.uniform(-0.9, 0.9), 2) % 360.0)
else:
joint.apply(probe, NO_SIGNAL)
self.assertFalse(joint.is_absent(), "真源可行集被观测清空")
assert_source = (round(source[0], 6), round(source[1], 6))
nearest = min((math.dist(joint.position.cell_center(index), assert_source)
for index in joint.position.alive),
default=float("inf"))
self.assertLessEqual(nearest, joint.position.margin_m + 1e-6,
"真源所在格被联合信念删除")
def test_repeated_directions_shrink_but_never_grow(self):
"""方向读数只会收缩联合集;贴测点的格按保守设计保留。"""
joint = JointBelief(CONFIG)
joint.apply((0.0, 0.0), DIRECTION, 0.0)
first = len(joint)
joint.apply((0.0, 0.0), DIRECTION, 180.0)
self.assertLessEqual(len(joint), first)
self.assertGreater(len(joint), 0)
def test_near_keeps_only_a_small_disk(self):
joint = JointBelief(CONFIG)
joint.apply((100.0, 100.0), NEAR)
center, radius = joint.position.mec()
# 观测更新内缩 Voronoi 裕量,故上界为 near + margin
self.assertLessEqual(radius, CONFIG.field.near_radius_m
+ joint.position.margin_m + 1.0)
self.assertLess(math.dist(center, (100.0, 100.0)), 15.0)
class ExpectedPosteriorRadiusTests(unittest.TestCase):
def _joint(self):
joint = JointBelief(CONFIG)
joint.apply((0.0, 0.0), DIRECTION, 0.0)
return joint
def test_value_is_finite_and_positive(self):
joint = self._joint()
for probe in ((800.0, 0.0), (0.0, 800.0), (-500.0, -500.0)):
value = joint.expected_posterior_radius(probe)
self.assertTrue(math.isfinite(value))
self.assertGreater(value, 0.0)
def test_blocked_reading_keeps_spatial_radius(self):
"""no_signal 不删空间候选:期望半径不应被误当作已收缩。"""
joint = self._joint()
before = joint.position.mec()[1]
joint.apply((1500.0, 0.0), NO_SIGNAL)
self.assertAlmostEqual(joint.position.mec()[1], before, places=6)
self.assertGreater(joint.expected_posterior_radius((1500.0, 0.0)),
0.5 * before)
def test_vector_and_python_paths_agree(self):
if _np is None:
self.skipTest("环境无 numpy:纯 Python 路径即默认实现")
joint = self._joint()
for probe in ((600.0, 100.0), (-300.0, 700.0)):
indices = joint.sample(200)
representatives = indices[::max(len(indices) // 7, 1)]
vector = joint._epr_vector(probe, indices, representatives)
python = joint._epr_python(probe, indices, representatives)
self.assertAlmostEqual(vector, python, places=6)
if __name__ == "__main__":
unittest.main()
+58 -1
View File
@@ -13,7 +13,7 @@ from dataclasses import replace
from random import Random
from mcm26b.config import ProblemConfig
from mcm26b.particles import ChannelParticles, angular_gap
from mcm26b.particles import ChannelParticles, angular_gap, as_sample_array
from mcm26b.protocol.rules import DIRECTION, NEAR, NO_SIGNAL
from mcm26b.planner.seven import seven_points
@@ -169,3 +169,60 @@ class QueryPrimitiveTests(unittest.TestCase):
if __name__ == "__main__":
unittest.main()
class VectorizedScoringParityTests(unittest.TestCase):
"""numpy 快路径与纯 Python 路径必须逐位同语义(方案 §P0-4)。
评分只用于选点,不参与安全证明;但两条路径若给出不同排序,会导致
同一动作日志在不同环境下不可复现,因此必须对齐。
"""
def _fixture(self):
config = ProblemConfig()
particles = ChannelParticles(config)
rng = Random(20260912)
subsample = [(rng.uniform(-1800, 1800), rng.uniform(-1800, 1800))
for _ in range(64)]
representatives = subsample[::8]
return particles, subsample, representatives
def test_expected_survival_matches(self):
particles, subsample, representatives = self._fixture()
for point in ((0.0, 0.0), (700.0, -300.0), (-1200.0, 900.0)):
fast = particles.expected_survival(point, as_sample_array(subsample),
as_sample_array(representatives))
slow = particles.expected_survival(point, subsample, representatives)
self.assertAlmostEqual(fast, slow, places=9)
def test_expected_posterior_radius_matches(self):
particles, subsample, representatives = self._fixture()
for point in ((0.0, 0.0), (700.0, -300.0), (-1200.0, 900.0)):
fast = particles.expected_posterior_radius(
point, as_sample_array(subsample),
as_sample_array(representatives))
slow = particles.expected_posterior_radius(
point, subsample, representatives)
self.assertAlmostEqual(fast, slow, places=9)
def test_fraction_within_and_branch_radius_match(self):
particles, subsample, _ = self._fixture()
array = as_sample_array(subsample)
for radius in (100.0, 1000.0, 1500.0):
self.assertAlmostEqual(
particles.fraction_within(array, (10.0, 20.0), radius),
particles.fraction_within(subsample, (10.0, 20.0), radius),
places=12)
self.assertAlmostEqual(particles._branch_radius(array),
particles._branch_radius(subsample), places=9)
def test_empty_samples_are_safe(self):
particles, _, _ = self._fixture()
self.assertEqual(particles.expected_survival((0.0, 0.0), [], []), 0.0)
self.assertEqual(
particles.expected_posterior_radius(
(0.0, 0.0), as_sample_array([]), as_sample_array([])), 0.0)
if __name__ == "__main__":
unittest.main()
+105
View File
@@ -0,0 +1,105 @@
"""图论 / DP 工具箱验收(docs/第三四问图论与动态规划优化方案.md §3.2、§3.4)。
要点:
- Held-Karp 精确开放路径必须**真的**最优(与全排列暴力对拍);
- 启发式(NN + 2-opt / Or-opt)必须是可接受的上界,且不劣于 NN;
- MST 权重必须是最优开放路径的合法下界(在线后悔值审计依赖它);
- 路由度量口径可复算:actual == offline 时 gap 为 0,且 gap 不越界。
"""
import itertools
import math
import random
import unittest
from mcm26b.routing import (EXACT_LIMIT, held_karp_open, improve_open_path,
mst_weight, nearest_neighbor_order,
optimal_open_path, or_opt_open, path_length,
route_metrics, two_opt_open)
def _random_points(count: int, seed: int, radius: float = 1500.0):
rng = random.Random(seed)
return [(rng.uniform(-radius, radius), rng.uniform(-radius, radius))
for _ in range(count)]
class HeldKarpTests(unittest.TestCase):
def test_matches_brute_force_on_small_instances(self):
for seed in range(6):
points = _random_points(6, seed)
order = held_karp_open(points)
best = min(path_length(list(perm))
for perm in itertools.permutations(points))
self.assertAlmostEqual(path_length(order), best, places=6,
msg=f"seed={seed}")
def test_returns_all_points_once(self):
points = _random_points(9, 42)
order = held_karp_open(points)
self.assertEqual(sorted(order), sorted(points))
def test_single_and_empty(self):
self.assertEqual(held_karp_open([]), [])
point = [(3.0, 4.0)]
self.assertEqual(held_karp_open(point), point)
class HeuristicTests(unittest.TestCase):
def test_two_opt_and_or_opt_never_worse_than_nn(self):
for seed in range(5):
points = _random_points(30, seed)
base = path_length(nearest_neighbor_order(points))
self.assertLessEqual(path_length(two_opt_open(
nearest_neighbor_order(points))), base + 1e-9)
self.assertLessEqual(path_length(improve_open_path(points)),
base + 1e-9)
def test_optimal_open_path_beats_heuristics_small(self):
points = _random_points(10, 7)
optimal = path_length(optimal_open_path(points))
self.assertLessEqual(optimal,
path_length(improve_open_path(points)) + 1e-9)
def test_or_opt_accepts_only_improvements(self):
points = _random_points(20, 3)
order = nearest_neighbor_order(points)
improved = or_opt_open(order)
self.assertLessEqual(path_length(improved), path_length(order) + 1e-9)
class LowerBoundTests(unittest.TestCase):
def test_mst_is_a_lower_bound(self):
for seed in range(8):
points = _random_points(24, seed)
optimum = path_length(optimal_open_path(points))
self.assertLessEqual(mst_weight(points), optimum + 1e-6,
msg=f"seed={seed}")
def test_mst_without_start(self):
points = _random_points(12, 1)
self.assertLessEqual(mst_weight(points, start=None),
path_length(optimal_open_path(points)) + 1e-6)
class RouteMetricsTests(unittest.TestCase):
def test_gap_zero_when_actual_matches_offline(self):
points = _random_points(8, 5)
offline = path_length(optimal_open_path(points))
metrics = route_metrics(points, offline)
self.assertAlmostEqual(metrics["route_gap_vs_offline"], 0.0, places=6)
self.assertEqual(metrics["distinct_service_points"], 8)
def test_dedupes_points(self):
points = [(1.0, 1.0), (1.0, 1.0), (2.0, 2.0)]
metrics = route_metrics(points, 10.0)
self.assertEqual(metrics["distinct_service_points"], 2)
def test_empty_is_safe(self):
metrics = route_metrics([], 0.0)
self.assertEqual(metrics["distinct_service_points"], 0)
self.assertEqual(metrics["route_gap_vs_offline"], 0.0)
if __name__ == "__main__":
unittest.main()
+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()