feat(solve): random_walk 的 step_sampler 契约改成"整数 size + 一维返回"(方案 B)

用户拍板方案 B(决策 55),Codex 代改。

- size 由 (n_paths, n_steps) 改成整数 n_paths * n_steps,必须返回一维;
  内部 reshape(n_paths, n_steps)(行主序);二维返回一律判失败
- 动因:方阵时转置 (n_steps, n_paths) 与目标形状相同,形状校验天生分不出来(易错点 25)
- 关键:不用 .ravel() 救场(会把转置摊平,歧义原样回来)
- 红利:与 monte_carlo 的 sampler 契约(决策 48 的 size=n)统一,采样器可直接复用(实测)
- docstring:step_sampler 契约与 Notes 同步(一维 / 行主序 / 二维必失败)
- tests:转置那条改成 rejects_2d_step_sampler(含方阵),新增"整数 size + 行主序"契约测试;solve 段 43 条
- 验证:210 收集 / 179 passed / 31 skipped / 0 xfail,ruff 0,pyright 0
- docs:STATUS 决策 55 + random_walk 待办全部办结;CHANGELOG
This commit is contained in:
guzhujushi committed 2026-10-05 18:06:55 +08:00
1 parent cd6f613568
commit 9368dd91cb
4 files changed
+73 -38

No files matched your search

+12 -6
View File
@@ -350,8 +350,9 @@ def random_walk(
n_paths:
路径条数,正整数,默认 1 条。
step_sampler:
`f(rng, size) -> 一批增量`。批量大小由本函数以 `size=(n_paths, n_steps)` 传入,
必须返回形状 `(n_paths, n_steps)` 的数组(契约同 `monte_carlo` 的 `sampler`,只是这里是二维)。
`f(rng, size) -> 一批增量`。**`size` 是整数** `n_paths * n_steps`(与 `monte_carlo` 的 `sampler` 同一契约),
必须返回**一维**长度 `n_paths * n_steps` 的数组;本函数负责 `reshape(n_paths, n_steps)`(行主序)。
返回二维一律判失败 —— 否则"转置的采样器"在 `n_paths == n_steps` 时会静默给出转置结果。
默认 ±1 等概率(对称随机游走)。
start:
起点,写进每条路径的第 0 列。
@@ -376,8 +377,9 @@ def random_walk(
随机游走是"平方根扩散"的原型:位置的标准差随步数按 `√t` 增长,所以典型图形是 `±√t` 的喇叭口。
本函数只负责 `cumsum`;**建模差异全在 `step_sampler` 里** —— 换成 `rng.normal(0, 1, size)` 就是高斯游走,
换成带正漂移的增量就是有趋势的游走。
`step_sampler` 返回的形状不等于 `(n_paths, n_steps)`、或增量里出现 NaN,都判 `success=False`
`step_sampler` 返回的不是**一维**、长度不等于 `n_paths * n_steps`,或增量里出现 NaN,都判 `success=False`
(NaN 口径与 `monte_carlo` 一致);其余异常同样统一走失败分支。
步与路径的对应关系是**行主序**:一维数组的前 `n_steps` 个元素属于第 1 条路径。
Examples
--------
@@ -417,12 +419,16 @@ def random_walk(
}
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}")
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,请检查采样逻辑")
steps = steps.reshape(n_paths, n_steps) # 行主序:前 n_steps 个属于第 1 条路径
cumsum = np.cumsum(steps, axis=1)
paths = np.zeros((n_paths, n_steps + 1), dtype=float)