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
@@ -305,9 +305,16 @@
|
||||
并写清 `step_sampler` 收的是**二维** `size=(n_paths, n_steps)`);③ Examples 增加一条"步长恒为 ±1"的断言
|
||||
- **测试:新增 6 条**(此前 `random_walk` **一条测试都没有**)—— 形状与 `start` 列、默认步长恒 ±1、
|
||||
同种子可复现、自定义高斯采样器(带漂移)、3 类非法入参、docstring Example 真能跑
|
||||
- ⏳ **留给用户 4 条**(见"下一步"):非整数 `n_steps` 该抛 `TypeError` 而现抛 `ValueError`;
|
||||
`steps` 形状没校验(`n_paths == n_steps` 时转置的采样器会**静默给出转置结果**);NaN 不判失败;
|
||||
`params` 少了 `random_state`、`metrics` 只是把 `n_steps` / `n_paths` 抄了一遍
|
||||
- **用户第二轮(2026-10-05)按 4 条待办改定**:`n_steps` / `n_paths` 拆成 `TypeError`(类型)/ `ValueError`(取值);
|
||||
加 `steps.shape != (n_paths, n_steps)` 与 NaN 两条校验;默认采样器换成 `rng.choice([-1.0, 1.0], size=size)`;
|
||||
`params` 补 `random_state`;`metrics` 由"抄 params"改成 `mean_terminal` / `std_terminal`(终点位置,`ddof=0`)
|
||||
- **Codex 第二轮又抓到一处真 bug(决策 31)**:用户把 `call_params = {...}` 写进了 `try` 里,而 `except` 分支要引用它 ——
|
||||
**5 种失败输入 5 个全炸** `UnboundLocalError`(本该返回 `success=False` 的 `Result`,变成把异常抛给调用方)。
|
||||
按原意把 `call_params` 移到 `try` 之前(不动任何口径),实测失败路径全部恢复正常;新增一条"必然失败"的回归测试(易错点 26)
|
||||
- **测试:本轮再 +3 → 共 9 条**(新增"失败也必须返回 Result 且带 params"、NaN 判失败、`√t` 扩散);
|
||||
`start="a"` 那条改为断言 `success is False`(因为用户删掉了 `start` 的入口类型校验,口径待定)
|
||||
- ⏳ **留给用户 3 条**(见"下一步"):`start` 的类型校验要不要恢复;`n_steps` / `n_paths` 签名注解仍是 `int` 却收 `np.int64`;
|
||||
`n_paths == n_steps` 时转置采样器仍无法被形状校验发现
|
||||
|
||||
- [ ] 剩余 1 项:`save_figure`(A 档 = 工程,**Codex 直接写**)
|
||||
|
||||
@@ -315,7 +322,7 @@
|
||||
|
||||
| 检查 | venv | conda `mcm-toolkit` |
|
||||
|---|---|---|
|
||||
| `pytest` | 见下(单列) | **174 passed, 31 skipped(0 xfail)** |
|
||||
| `pytest` | 见下(单列) | **177 passed, 31 skipped(0 xfail)** |
|
||||
| `import mcm_toolkit` | 0.0.1 / 16 项 | 0.0.1 / 16 项 |
|
||||
| `ruff check` | All checks passed | **All checks passed**(0 error) |
|
||||
| `scripts/doctor.ps1` | 跑通 | — |
|
||||
@@ -325,8 +332,8 @@
|
||||
(修配置之前是 17 errors,全是"无法解析导入 numpy / pandas",见决策 29 与
|
||||
`docs/04-environment.md` 坑 7)
|
||||
- base 亦已复核:numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6(与两套环境一致)
|
||||
- **205 条用例:174 passed / 31 skipped / 0 xfail**。收集数按文件:`test_data.py` 52、`test_api_surface.py` 20、
|
||||
`test_core.py` 23、`test_solve.py` 38(本轮 +6:`random_walk` 首次有测试)、`test_viz.py` 8、`test_decision.py` 7、
|
||||
- **208 条用例:177 passed / 31 skipped / 0 xfail**。收集数按文件:`test_data.py` 52、`test_api_surface.py` 20、
|
||||
`test_core.py` 23、`test_solve.py` 41(本轮 +3:失败路径 / NaN / `√t` 扩散)、`test_viz.py` 8、`test_decision.py` 7、
|
||||
`test_stats.py` 53、`test_models.py` 5
|
||||
- **`xfail` 再次清零**:`correlation_matrix` 的契约冲突已按方案 A 解决(决策 41),那条用例转正
|
||||
- 上一轮那 7 条 xfail 已全部转正,决策 30 那套信号(XPASS → 摘 mark)跑完过一个完整闭环
|
||||
@@ -475,16 +482,23 @@ docstring 与 `test_describe_reports_expected_keys` 早就写死了这个方向
|
||||
`step_sampler` 的 `size` 是 `(n_paths, n_steps)` 而不是 `monte_carlo` 那样的一个整数 —— 契约已写进 docstring,
|
||||
**遗留 4 条待办**见"下一步"(`TypeError`/`ValueError` 分工、`steps` 形状校验、NaN 口径、`params` 缺 `random_state`)。
|
||||
|
||||
| 54 | `random_walk` 第二轮口径由**用户**改定:`n_steps`/`n_paths` 类型错抛 `TypeError`、取值错抛 `ValueError`;加形状与 NaN 两条校验;默认步长改 `rng.choice([-1.0, 1.0])`;`params` 补 `random_state`;`metrics` 改 `mean_terminal`/`std_terminal` |
|
||||
用户 2026-10-05 自行改定,Codex 只修了一处**纯 bug**:`call_params` 定义在 `try` 内、却被 `except` 引用,
|
||||
导致**所有失败路径**抛 `UnboundLocalError`(实测 5/5 失败输入全中)。按原意前移到 `try` 之前,未改任何口径。
|
||||
**遗留 3 条**:① `start` 的入口类型校验被删(`start="a"` 从 `TypeError` 变成 `success=False`),是有意还是顺手删的?
|
||||
② 签名 `n_steps: int` / `n_paths: int` 与运行期收 `np.int64` 不一致(§4.7;`monte_carlo_integral` 的 `n` 已放宽);
|
||||
③ `n_paths == n_steps` 时,返回转置形状的采样器仍会静默通过(形状校验天生发现不了,见易错点 25)。
|
||||
|
||||
## 下一步
|
||||
|
||||
### ⭐ 当前进度与待办(2026-10-05 第三轮)
|
||||
### ⭐ 当前进度与待办(2026-10-05 第四轮)
|
||||
|
||||
**本轮:`random_walk` 已由用户写完第一版并审查通过**(L1,S 档)。Codex 改了一处**真 bug**
|
||||
(默认采样器 `rng.integers(..., dtype=float)` 是 numpy 不支持的写法,默认路径 100% 失败,见决策 53),
|
||||
另补 ruff / docstring / **6 条从无到有的测试**。
|
||||
**本轮:`random_walk` 第二版(用户按 4 条待办改定)已审查通过**。Codex 又抓到一处**真 bug**:
|
||||
`call_params` 定义在 `try` 内、却在 `except` 里被引用,**所有失败路径**都抛 `UnboundLocalError`(易错点 26,见决策 54);
|
||||
另补 docstring、+3 条测试(共 9 条)。
|
||||
|
||||
**接下来三件事**
|
||||
1. `random_walk` 有 **4 条待办**(下面一节)——都是你的活。
|
||||
1. `random_walk` 的 4 条待办已办结,新增 **3 条**(下面一节)——都是你的活。
|
||||
2. `ci_low` / `ci_high` 的语义确认一句(也是你的活),确认后 Codex 补 `docs/algorithms/monte_carlo.md` 卡片。
|
||||
3. L0 最后一项 `save_figure`(A 档 = 工程,`viz/export.py`)—— **Codex 直接实现**,你只读。
|
||||
|
||||
@@ -492,19 +506,25 @@ docstring 与 `test_describe_reports_expected_keys` 早就写死了这个方向
|
||||
两者是同一件事的两面:前者按转移矩阵**逐代抽状态**(`rng.choice` 的 `p=` 版),后者用**迭代**求平稳分布。
|
||||
`markov_steady_state` 的 docstring 已写明口径:用迭代而不用特征分解,因为"收敛快不快"本身就是论文里要写的性质。
|
||||
|
||||
### ⏳ random_walk 的 4 条待办(2026-10-05,你的活)
|
||||
### ✅ random_walk 的 4 条待办(2026-10-05 已办结)
|
||||
|
||||
1. **非整数 `n_steps` 抛的是 `ValueError`,按 §4.7 该是 `TypeError`**:现在 `n_steps=2.5` / `n_steps=True` 都走
|
||||
`ValueError("必须是正整数")`。同模块的 `monte_carlo` / `monte_carlo_probability` 是"类型不对 → `TypeError`、
|
||||
取值不对(`<= 0`)→ `ValueError`",这里要拆成两条。
|
||||
2. **`steps` 没有形状校验,会静默出错**:实测 `n_paths == n_steps` 时,一个返回**转置**形状的采样器
|
||||
能拿到 `success=True` 和形状"正确"的 `data` —— 数字却是转置过来的(`n_paths != n_steps` 时才报错)。
|
||||
方向:`steps.shape != (n_paths, n_steps)` 就判失败,和 `monte_carlo` 的 `len(data) != n` 一个套路。
|
||||
3. **NaN 不判失败**:`step_sampler` 返回全 NaN 时现在照样 `success=True`(决策 48 里 `monte_carlo` 是判失败的)——
|
||||
两个函数的 NaN 口径要不要拉齐?
|
||||
4. **`params` 少了 `random_state`**(另两个函数都记了,`Result.params` 的用途就是"能复现");
|
||||
另外 `metrics={"n_steps", "n_paths"}` 只是把 `params` 里的两个数抄了一遍 —— 要么换成真正的指标
|
||||
(终点的均值 / 标准差、理论 `√t` 对比),要么直接去掉。
|
||||
- ~~① 非整数 `n_steps` 抛 `ValueError`~~ → 已拆成两条:类型错 `TypeError`、取值错(`<= 0`)`ValueError`,`n_paths` 同样处理。
|
||||
- ~~② `steps` 没有形状校验~~ → 已加 `steps.shape != (n_paths, n_steps)` 判失败(实测转置采样器现在能拦住)。
|
||||
- ~~③ NaN 不判失败~~ → 已加 `np.isnan(steps).any()`,与 `monte_carlo` 的 NaN 口径(决策 48)拉齐。
|
||||
- ~~④ `params` 少 `random_state` / `metrics` 抄了一遍~~ → 已补 `random_state`;`metrics` 换成 `mean_terminal` / `std_terminal`
|
||||
(终点位置的均值与标准差,`ddof=0`)—— 这才是"游走"该看的指标,测试里还顺手验了 `std ≈ √t`。
|
||||
|
||||
### ⏳ random_walk 还有 3 条口径(新增,你的活)
|
||||
|
||||
1. **`start` 的入口类型校验被删了**:`start="a"` 现在走 `success=False`("could not convert string to float"),
|
||||
不再是入口的 `TypeError`。是有意改成"交给失败分支"(那 docstring 里我已如实写明"入口不校验类型"),还是顺手删的?
|
||||
要恢复的话,原来那行是 `if not isinstance(start, (int, float, np.number)): raise TypeError(...)`。
|
||||
2. **签名注解比行为窄**:`n_steps: int` / `n_paths: int`,但入口校验明明收 `np.integer`(实测 `np.int64` 能过)。
|
||||
按 §4.7 应与 `monte_carlo_integral` 的 `n` 一样写成 `int | np.integer` —— 现在 pyright 会对老老实实按注解传参的
|
||||
调用方报 `reportArgumentType`(我写测试时就被拦了一次,所以那条测试暂时撤了)。
|
||||
3. **`n_paths == n_steps` 时形状校验天生失效**:转置的采样器返回 `(n_steps, n_paths)`,正好等于 `(n_paths, n_steps)`,
|
||||
校验放行、结果静默转置(易错点 25)。要么接受这个边界,要么让 `step_sampler` 契约改成"返回 `(n_paths, n_steps)` 之外的形状即失败"
|
||||
之外的更强约束(比如显式要求 `size` 是 tuple 并校验每个维度)—— 你定。
|
||||
|
||||
### ✅ monte_carlo_probability 的 6 条待办(2026-10-05 全部办结)
|
||||
|
||||
|
||||
Reference in new issue
Block a user