feat(solve): 审查 markov_steady_state:工程侧收口 + 8 条测试

用户实现的 markov_steady_state 经探针实测算法本体正确(3 状态与特征分解逐位一致、
200x200 一致到 1.9e-12 且只用 6 次迭代 / 0.10s),本次只动工程侧,未改任何算法行:

- ruff 5 条 W293 全清;mm1_metrics 前 4 个空行 -> 2;失败文案改成全角冒号
- docstring 补 max_iter / tol 两个参数与 Raises,写清 tol 是相邻差口径
  (实测慢混合链 tol=1e-10 -> 实际误差 4.6e-9,被 1/(1-|lambda2|) 放大约 50 倍)
- Notes 修正"周期链会不收敛"的说法:可约链的实情是平稳分布不唯一,
  迭代法给的是离初值最近的那个(均匀初值对双随机矩阵本身就是不动点)
- tests/test_solve.py:摘掉 1 条 skip + 新增 7 条(该函数共 8 条实跑)
- STATUS.md 新增决策 57(6 条待拍板口径)与第六轮「下一步」;CHANGELOG;易错点 28

校验:pytest 196 passed / 30 skipped(226 收集);ruff 0;pyright 0
This commit is contained in:
guzhujushi committed 2026-10-09 23:20:13 +08:00
1 parent dd9bd638b8
commit 0955890010
5 files changed
+263 -27

No files matched your search

+76 -3
View File
@@ -508,15 +508,88 @@ def test_markov_simulate_docstring_example_runs():
assert np.all(r.data[:, 0] == 0)
@pytest.mark.skip(reason=_NOT_IMPLEMENTED)
def test_markov_steady_state_two_state_chain():
"""P=[[0.9,0.1],[0.2,0.8]] 的平稳分布是 [2/3, 1/3]。"""
from mcm_toolkit.solve import markov_steady_state
r = markov_steady_state(np.array([[0.9, 0.1], [0.2, 0.8]]))
assert r.success
assert np.allclose(r.data, [2 / 3, 1 / 3], atol=1e-6)
assert r.metrics["converged"] is True
assert r.metrics["iterations"] > 0
def test_markov_steady_state_matches_eigenvector():
"""与特征分解对拍:取特征值 1 对应的左特征向量(只是检验手段,实现仍走迭代)。"""
from mcm_toolkit.solve import markov_steady_state
P = np.array([[0.5, 0.25, 0.25], [0.1, 0.8, 0.1], [0.2, 0.2, 0.6]])
eigenvalues, eigenvectors = np.linalg.eig(P.T)
ref = np.real(eigenvectors[:, int(np.argmin(np.abs(eigenvalues - 1.0)))])
ref = ref / ref.sum()
r = markov_steady_state(P)
assert np.allclose(r.data, ref, atol=1e-6)
def test_markov_steady_state_is_a_distribution():
from mcm_toolkit.solve import markov_steady_state
P = np.array([[0.7, 0.2, 0.1], [0.1, 0.6, 0.3], [0.05, 0.15, 0.8]])
r = markov_steady_state(P)
assert r.data.shape == (3,)
assert np.isclose(float(np.sum(r.data)), 1.0)
assert np.all(r.data >= 0.0)
assert r.data.dtype == np.float64
def test_markov_steady_state_slow_mixing_needs_more_iterations():
"""慢混合链:迭代次数显著变多,但结果仍要对得上特征向量(tol 是相邻差口径)。"""
from mcm_toolkit.solve import markov_steady_state
P = np.array([[0.999, 0.001], [0.02, 0.98]])
r = markov_steady_state(P)
assert r.metrics["iterations"] > 100
assert np.allclose(r.data, [0.9523809524, 0.0476190476], atol=1e-6)
def test_markov_steady_state_accepts_numpy_integer():
"""§4.7:`np.int64` 不是 `int` 的子类,注解与运行期都要收(易错点 1)。"""
from mcm_toolkit.solve import markov_steady_state
P = np.array([[0.9, 0.1], [0.2, 0.8]])
r = markov_steady_state(P, max_iter=np.int64(10000))
assert r.success
assert r.metrics["converged"] is True
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(ValueError):
markov_steady_state(np.array([[0.5, 0.4], [0.2, 0.8]]))
def test_markov_steady_state_rejects_bad_arguments():
"""取值错一律 `ValueError`(类型错要不要拆 `TypeError` 待定,见 STATUS 决策 57)。"""
from mcm_toolkit.solve import markov_steady_state
P = np.array([[0.9, 0.1], [0.2, 0.8]])
with pytest.raises(ValueError):
markov_steady_state(P, max_iter=0)
with pytest.raises(ValueError):
markov_steady_state(P, tol=0)
def test_markov_steady_state_docstring_example_runs():
from mcm_toolkit.solve import markov_steady_state
P = np.array([[0.9, 0.1], [0.2, 0.8]])
r = markov_steady_state(P)
assert bool(np.allclose(r.data, [2 / 3, 1 / 3], atol=1e-6))
@pytest.mark.skip(reason=_NOT_IMPLEMENTED)