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
@@ -354,27 +354,29 @@ def random_walk(
|
||||
必须返回形状 `(n_paths, n_steps)` 的数组(契约同 `monte_carlo` 的 `sampler`,只是这里是二维)。
|
||||
默认 ±1 等概率(对称随机游走)。
|
||||
start:
|
||||
起点,写进每条路径的第 0 列。
|
||||
起点,写进每条路径的第 0 列(**入口不校验类型**,传非数值会落到失败分支)。
|
||||
random_state:
|
||||
随机种子。`None` 时跟随 `core.set_seed` 的全局流,见 `core.random.get_rng`。
|
||||
|
||||
Returns
|
||||
-------
|
||||
`Result`:`data` 形状 `(n_paths, n_steps + 1)`,第 0 列恒为 `start`,之后是逐列累加出来的位置;
|
||||
`metrics={"n_steps", "n_paths"}`。
|
||||
`metrics={"mean_terminal", "std_terminal"}`(终点的均值与标准差,`std` 用 `ddof=0`)。
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError:
|
||||
`n_steps` / `n_paths` 不是整数。
|
||||
ValueError:
|
||||
`n_steps` / `n_paths` 不是正整数。
|
||||
TypeError:
|
||||
`start` 不是数值。
|
||||
|
||||
Notes
|
||||
-----
|
||||
随机游走是"平方根扩散"的原型:位置的标准差随步数按 `√t` 增长,所以典型图形是 `±√t` 的喇叭口。
|
||||
本函数只负责 `cumsum`;**建模差异全在 `step_sampler` 里** —— 换成 `rng.normal(0, 1, size)` 就是高斯游走,
|
||||
换成带正漂移的增量就是有趋势的游走。
|
||||
`step_sampler` 返回的形状不等于 `(n_paths, n_steps)`、或增量里出现 NaN,都判 `success=False`
|
||||
(NaN 口径与 `monte_carlo` 一致);其余异常同样统一走失败分支。
|
||||
|
||||
Examples
|
||||
--------
|
||||
@@ -385,23 +387,37 @@ def random_walk(
|
||||
>>> bool(np.all(np.abs(np.diff(r.data, axis=1)) == 1)) # 默认步长恒为 1
|
||||
True
|
||||
"""
|
||||
if isinstance(n_steps, bool) or not isinstance(n_steps, (int, np.integer)) or n_steps <= 0:
|
||||
if isinstance(n_steps, bool) or not isinstance(n_steps, (int, np.integer)):
|
||||
raise TypeError(f"n_steps 必须是整数,你传入了 {type(n_steps).__name__}")
|
||||
if n_steps <= 0:
|
||||
raise ValueError(f"n_steps 必须是正整数,你传入了 {n_steps}")
|
||||
if isinstance(n_paths, bool) or not isinstance(n_paths, (int, np.integer)) or n_paths <= 0:
|
||||
|
||||
if isinstance(n_paths, bool) or not isinstance(n_paths, (int, np.integer)):
|
||||
raise TypeError(f"n_paths 必须是整数,你传入了 {type(n_paths).__name__}")
|
||||
if n_paths <= 0:
|
||||
raise ValueError(f"n_paths 必须是正整数,你传入了 {n_paths}")
|
||||
if not isinstance(start, (int, float, np.number)):
|
||||
raise TypeError(f"start 必须是数值,你传入了 {type(start).__name__}")
|
||||
|
||||
rng = get_rng(random_state)
|
||||
|
||||
if step_sampler is None:
|
||||
def _default_step_sampler(rng, size):
|
||||
return rng.integers(0, 2, size=size) * 2 - 1
|
||||
return rng.choice([-1.0, 1.0], size=size)
|
||||
|
||||
step_sampler = _default_step_sampler
|
||||
|
||||
call_params = {
|
||||
"n_steps": n_steps,
|
||||
"n_paths": n_paths,
|
||||
"start": start,
|
||||
"random_state": random_state,
|
||||
}
|
||||
|
||||
try:
|
||||
steps = np.asarray(step_sampler(rng, size=(n_paths, n_steps)), dtype=float)
|
||||
if steps.shape != (n_paths, n_steps):
|
||||
raise ValueError(f"step_sampler 返回形状必须为 ({n_paths}, {n_steps}),实际得到了 {steps.shape}")
|
||||
if np.isnan(steps).any():
|
||||
raise ValueError("step_sampler 生成的增量数据中包含 NaN,请检查采样逻辑")
|
||||
|
||||
cumsum = np.cumsum(steps, axis=1)
|
||||
|
||||
@@ -409,10 +425,16 @@ def random_walk(
|
||||
paths[:, 0] = start
|
||||
paths[:, 1:] = start + cumsum
|
||||
|
||||
terminal_positions = paths[:, -1] # 取最后一列,得到 n_paths 个终点位置
|
||||
metrics = {
|
||||
"mean_terminal": float(np.mean(terminal_positions)),
|
||||
"std_terminal": float(np.std(terminal_positions))
|
||||
}
|
||||
|
||||
return Result(
|
||||
name="random_walk",
|
||||
params={"n_steps": n_steps, "n_paths": n_paths, "start": start},
|
||||
metrics={"n_steps": n_steps, "n_paths": n_paths},
|
||||
params=call_params,
|
||||
metrics=metrics,
|
||||
data=paths,
|
||||
success=True,
|
||||
message=f"随机游走模拟成功:生成了 {n_paths} 条路径,每条路径 {n_steps} 步。"
|
||||
@@ -420,7 +442,7 @@ def random_walk(
|
||||
except Exception as e:
|
||||
return Result(
|
||||
name="random_walk",
|
||||
params={"n_steps": n_steps, "n_paths": n_paths, "start": start},
|
||||
params=call_params,
|
||||
metrics={},
|
||||
data=None,
|
||||
success=False,
|
||||
|
||||
Reference in new issue
Block a user