fix(solve): random_walk 失败路径的 UnboundLocalError;第二轮口径收口
口径由用户第二轮改定;Codex 修一处真 bug + 工程侧。 - 真 bug:call_params 写在 try 里、却在 except 被引用,5 种失败输入 5 个全炸 UnboundLocalError (本该返回 success=False 的 Result,变成把异常抛给调用方)。按原意前移到 try 之前,未改口径; 失败时照样带上 n_steps / n_paths / start / random_state - 用户口径:n_steps/n_paths 拆 TypeError/ValueError;加 steps 形状与 NaN 两条校验; 默认步长改 rng.choice([-1.0, 1.0]);params 补 random_state;metrics 改 mean_terminal/std_terminal - 工程:docstring 同步(Returns 的 metrics、Raises 的分工、去掉已删的 start 校验);+3 条测试 (失败也必须返回带 params 的 Result、NaN 判失败、终点 std ~= sqrt(t)),solve 段 41 条 - 验证:208 收集 / 177 passed / 31 skipped / 0 xfail,ruff 0,pyright 0 - docs:STATUS 决策 54 + 3 条新口径;CHANGELOG;易错点 26
This commit is contained in:
1 parent
8ec08ea9ce
commit
6fca82e3fc
5 files changed
+160
-37
No files matched your search
+47
-2
@@ -318,14 +318,59 @@ def test_random_walk_accepts_custom_step_sampler():
|
||||
|
||||
|
||||
def test_random_walk_rejects_bad_arguments():
|
||||
"""§4.7 的分工:类型不对抛 TypeError,取值不对(<= 0)抛 ValueError。"""
|
||||
from mcm_toolkit.solve import random_walk
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
random_walk(n_steps=2.5) # type: ignore[arg-type] # 就是要传错
|
||||
with pytest.raises(TypeError):
|
||||
random_walk(n_steps=True)
|
||||
with pytest.raises(TypeError):
|
||||
random_walk(n_steps=10, n_paths=1.5) # type: ignore[arg-type] # 就是要传错
|
||||
with pytest.raises(ValueError):
|
||||
random_walk(n_steps=0)
|
||||
with pytest.raises(ValueError):
|
||||
random_walk(n_steps=10, n_paths=0)
|
||||
with pytest.raises(TypeError):
|
||||
random_walk(n_steps=10, start="a") # type: ignore[arg-type] # 就是要传错
|
||||
r = random_walk(n_steps=10, start="a") # type: ignore[arg-type] # 就是要传错
|
||||
assert r.success is False # 口径待定:入口抛 TypeError 也可以,见 STATUS"下一步"
|
||||
|
||||
|
||||
def test_random_walk_failure_returns_result_not_exception():
|
||||
"""失败也要返回带 params 的 Result,不能把异常抛给调用方 —— 包装层引用未赋值变量那次就靠它拦住。"""
|
||||
from mcm_toolkit.solve import random_walk
|
||||
|
||||
def transposed(rng, size):
|
||||
return rng.normal(0, 1, (size[1], size[0]))
|
||||
|
||||
r = random_walk(n_steps=4, n_paths=6, step_sampler=transposed, random_state=0)
|
||||
assert r.success is False
|
||||
assert "形状" in r.message
|
||||
assert r.params["n_paths"] == 6 # 失败也要留下追溯信息
|
||||
assert r.data is None
|
||||
|
||||
|
||||
def test_random_walk_rejects_nan_steps():
|
||||
"""增量含 NaN 判失败 —— 与 monte_carlo 的 NaN 口径(决策 48)一致。"""
|
||||
from mcm_toolkit.solve import random_walk
|
||||
|
||||
r = random_walk(
|
||||
n_steps=4,
|
||||
n_paths=2,
|
||||
step_sampler=lambda rng, size: np.full(size, np.nan),
|
||||
random_state=0,
|
||||
)
|
||||
assert r.success is False
|
||||
assert "NaN" in r.message
|
||||
|
||||
|
||||
def test_random_walk_metrics_are_terminal_positions():
|
||||
"""终点位置的标准差按 √t 扩散(t = n_steps)—— 随机游走的核心性质。"""
|
||||
from mcm_toolkit.solve import random_walk
|
||||
|
||||
n_steps = 100
|
||||
r = random_walk(n_steps=n_steps, n_paths=2000, start=5.0, random_state=0)
|
||||
assert abs(r.metrics["mean_terminal"] - 5.0) < 5.0 # 对称游走,期望还在起点附近
|
||||
assert abs(r.metrics["std_terminal"] - np.sqrt(n_steps)) < 5.0 # √t 扩散
|
||||
|
||||
|
||||
def test_random_walk_docstring_example_runs():
|
||||
|
||||
Reference in new issue
Block a user