fix(solve): 同步第三轮入口口径(docstring + 测试 + lint)

用户第三轮改了 5 处口径:非负校验带容差、initial 收 np.integer、paths 赋值挪进 try、
metrics 换成 terminal_distribution、mm1 放开 lambda=0 并收 numpy 标量。三处算法本体仍正确
(探针复测:终点分布 {0: 0.6644, 1: 0.3356}、lambda=0 给 L=Lq=Wq=0 且 W=1/mu、data is metrics 已为 False)。

Changed(Codex 只做同步,未改口径)
- docstring:markov_simulate 的 Returns 与"遗留"段、markov_steady_state 的 Raises
  (TypeError / ValueError 分工)、mm1_metrics 的 Parameters / Returns / Raises。
- tests/test_solve.py:同步 5 条受影响的测试,新增 2 条(terminal_distribution 只列出现过的状态、
  lambda=0 的空系统)。test_solve.py 64 条。
- ruff:B905(zip 缺 strict)+ 2 条空白 -> 0;rng 之后的双空行 -> 1。
- STATUS.md:决策 56 / 57 / 58 更新到"只剩 5 条小口径";「下一步」第八轮;
  新增「第八轮备忘:models 与 decision 各是干什么的」(函数清单 / 使用顺序 / 建议开工顺序)。
- CHANGELOG 与 docs/dev/personal-pitfalls.md 易错点 30(口径一改,docstring / 测试 / STATUS 同时过期)。

校验:pytest 204 passed / 28 skipped(232 收集);ruff 0;pyright 0
This commit is contained in:
guzhujushi committed 2026-10-10 09:31:30 +08:00
1 parent 953e988983
commit 9cb295a362
5 files changed
+190 -107

No files matched your search

+36 -9
View File
@@ -428,7 +428,7 @@ def test_markov_simulate_absorbing_states_never_move():
assert r.success
assert np.all(r.data == 1)
assert r.metrics["mean_terminal"] == 1.0
assert r.metrics["std_terminal"] == 0.0
assert r.metrics["terminal_distribution"] == {1: 1.0}
def test_markov_simulate_is_reproducible():
@@ -453,12 +453,25 @@ def test_markov_simulate_terminal_frequency_matches_stationary():
def test_markov_simulate_metrics_are_terminal_states():
"""`metrics` 只看终点:均值 + `{状态: 频率}`(频率之和为 1、与逐条统计一致)。"""
from mcm_toolkit.solve import markov_simulate
P = np.array([[0.9, 0.1], [0.2, 0.8]])
r = markov_simulate(P, initial=0, steps=50, n_paths=500, random_state=3)
assert r.metrics["mean_terminal"] == float(np.mean(r.data[:, -1]))
assert r.metrics["std_terminal"] == float(np.std(r.data[:, -1]))
terminal = r.data[:, -1]
dist = r.metrics["terminal_distribution"]
assert r.metrics["mean_terminal"] == float(np.mean(terminal))
assert np.isclose(sum(dist.values()), 1.0)
for state, freq in dist.items():
assert np.isclose(freq, float(np.mean(terminal == state)))
def test_markov_simulate_terminal_distribution_only_lists_seen_states():
"""只含实际出现过的状态(没出现的状态没有 key)—— docstring 已写明这条口径。"""
from mcm_toolkit.solve import markov_simulate
r = markov_simulate(np.eye(3), initial=2, steps=5, n_paths=4, random_state=0)
assert r.metrics["terminal_distribution"] == {2: 1.0}
def test_markov_simulate_rejects_bad_transition():
@@ -568,8 +581,8 @@ def test_markov_steady_state_accepts_numpy_integer():
def test_markov_steady_state_rejects_bad_transition():
from mcm_toolkit.solve import markov_steady_state
with pytest.raises(ValueError):
markov_steady_state(np.ones((2, 3)) / 3)
with pytest.raises(TypeError):
markov_steady_state(np.ones((2, 3)) / 3) # 形状不对 → TypeError(口径见决策 57)
with pytest.raises(ValueError):
markov_steady_state(np.array([[0.5, 0.4], [0.2, 0.8]]))
with pytest.raises(ValueError):
@@ -650,19 +663,33 @@ def test_mm1_rejects_unstable_system():
equal = mm1_metrics(arrival_rate=3, service_rate=3)
assert equal.success is False
assert "不稳定" in equal.message
assert equal.metrics == {}
assert equal.metrics == {"rho": 1.0} # 失败时也把 rho 报出来(决策 58 的 ④)
assert equal.data is None
assert mm1_metrics(arrival_rate=3, service_rate=2).success is False
def test_mm1_allows_zero_arrival_rate_as_empty_system():
"""λ = 0 是合法退化解:空系统(L = Lq = Wq = 0,W 取极限值 1/μ)。"""
from mcm_toolkit.solve import mm1_metrics
r = mm1_metrics(arrival_rate=0, service_rate=3)
assert r.success
assert r.metrics["rho"] == 0.0
assert r.metrics["L"] == 0.0
assert r.metrics["Lq"] == 0.0
assert r.metrics["Wq"] == 0.0
assert np.isclose(r.metrics["W"], 1 / 3)
def test_mm1_rejects_bad_arguments():
"""λ / μ 必须是正数:0、负数、bool、非数值都要在入口拦下。"""
"""λ 非负、μ 必须为正;bool 与非数值都要在入口拦下。"""
from mcm_toolkit.solve import mm1_metrics
with pytest.raises(ValueError):
mm1_metrics(arrival_rate=0, service_rate=3)
with pytest.raises(ValueError):
mm1_metrics(arrival_rate=-1, service_rate=3)
with pytest.raises(ValueError):
mm1_metrics(arrival_rate=2, service_rate=0)
with pytest.raises(TypeError):
mm1_metrics(arrival_rate=True, service_rate=3)
with pytest.raises(TypeError):