feat(solve): random_walk 落地;修默认采样器真 bug(integers 不收 float)
random_walk 由用户本人写;Codex 改一处真 bug + 工程侧。 - 真 bug:默认采样器 rng.integers(0, 2, size=size, dtype=float) 被 numpy 拒掉 (Unsupported dtype dtype(float64) for integers),默认路径 100% success=False、 data=None,连 docstring 的 Examples 都跑不通。改成 integers(size=size) * 2 - 1, 由后面的 np.asarray(..., dtype=float) 转 float;实测 (5,101)、步长恒 ±1、同种子可复现 - 工程:ruff W293 x2;docstring 补全(n_steps/n_paths/start/random_state + Returns / Raises / Notes,写清 step_sampler 收二维 size=(n_paths, n_steps)) - tests:random_walk 首次有测试,新增 6 条(形状 + start、步长恒 ±1、可复现、自定义 高斯采样器、3 类非法入参、docstring Example) - monte_carlo_integral:用户第二轮已收 ndarray + 把三行提出 if/else(决策 52) - 验证:205 收集 / 174 passed / 31 skipped / 0 xfail,ruff 0,pyright 0 - docs:STATUS 决策 53 + random_walk 4 条待办;CHANGELOG;易错点 24/25,第 18 条复发 +1
This commit is contained in:
1 parent
dcda080be4
commit
8ec08ea9ce
5 files changed
+254
-32
No files matched your search
@@ -21,7 +21,7 @@
|
||||
当前 **L0 11/12**:`Result`、`get_rng`/`set_seed`、`load_table`/`save_table`、`clean_missing`、
|
||||
`detect_outliers`、`normalize`/`standardize`、`describe`、`correlation_matrix`、`bootstrap_ci`、
|
||||
`monte_carlo`(**用户写**,2026-10-04);另**提前落地**了 L1 的 `confidence_interval` / `t_test` /
|
||||
`monte_carlo_probability`、`monte_carlo_integral`(**用户写**,2026-10-05)。剩余 1 项:`save_figure`(Codex 写)。
|
||||
`monte_carlo_probability`、`monte_carlo_integral`、`random_walk`(**用户写**,2026-10-05)。剩余 1 项:`save_figure`(Codex 写)。
|
||||
`monte_carlo` 的口径已拍板(决策 48);概率 / 积分两个函数的待办**均已办结**,
|
||||
剩下 `ci_low`/`ci_high` 的语义与 `monte_carlo_integral` 的 2 条新口径,见"下一步"。
|
||||
## 任务清单
|
||||
@@ -292,13 +292,30 @@
|
||||
- ✅ **3 条待办已由用户第二轮办结**(同日):`message` 改成 `if result.success:` / `else:` 两支(失败语义不再被覆盖);
|
||||
入口补 `isinstance(bounds, (list, tuple))` + `ndim == 2 and shape[1] == 2` 两道(`[0, 1]` 从裸 `IndexError` 变成可读 `ValueError`);
|
||||
`n` 注解放宽为 `int | np.integer`。⏳ 尚有 2 条新口径(`bounds` 收不收 ndarray、`if`/`else` 三行重复)见"下一步"
|
||||
- [x] `solve/simulate.py` 的 `random_walk`(**L1,S 档,算法由用户本人写**,2026-10-05)
|
||||
- 核心:`step_sampler(rng, size=(n_paths, n_steps))` 一次抽完整批增量 → `np.cumsum(..., axis=1)` →
|
||||
首列填 `start`、其余列 `start + cumsum`。`data` 形状 `(n_paths, n_steps + 1)`
|
||||
- **Codex 改了一处真 bug(决策 31,纯 bug)**:默认采样器写 `rng.integers(0, 2, size=size, dtype=float)`,
|
||||
而 numpy 的 `integers` 不收 `dtype=float`(`TypeError: Unsupported dtype dtype('float64') for integers`)
|
||||
—— **默认路径 100% 失败**(`success=False`、`data=None`),连 docstring 的 Examples 都跑不通。
|
||||
改成 `rng.integers(0, 2, size=size) * 2 - 1`(后面的 `np.asarray(..., dtype=float)` 负责转 float)。
|
||||
实测:`(5, 101)` 形状、`diff` 恒为 ±1、同种子可复现
|
||||
- **Codex 只碰工程侧**:① ruff 两处 W293(只有空格的行);② docstring 补全(原只有 `step_sampler` 一节,
|
||||
现补齐 `n_steps` / `n_paths` / `start` / `random_state` + `Returns` / `Raises` / `Notes`,
|
||||
并写清 `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` 抄了一遍
|
||||
|
||||
- [ ] 剩余 1 项:`save_figure`(A 档 = 工程,**Codex 直接写**)
|
||||
|
||||
### 验证结果
|
||||
|
||||
| 检查 | venv | conda `mcm-toolkit` |
|
||||
|---|---|---|
|
||||
| `pytest` | 见下(单列) | **168 passed, 31 skipped(0 xfail)** |
|
||||
| `pytest` | 见下(单列) | **174 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` | 跑通 | — |
|
||||
@@ -308,8 +325,8 @@
|
||||
(修配置之前是 17 errors,全是"无法解析导入 numpy / pandas",见决策 29 与
|
||||
`docs/04-environment.md` 坑 7)
|
||||
- base 亦已复核:numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6(与两套环境一致)
|
||||
- **199 条用例:168 passed / 31 skipped / 0 xfail**。收集数按文件:`test_data.py` 52、`test_api_surface.py` 20、
|
||||
`test_core.py` 23、`test_solve.py` 32(本轮再 +2:非法 `bounds` 扩到 4 类 + 失败 message 语义 / `np.int64`)、`test_viz.py` 8、
|
||||
- **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、
|
||||
`test_stats.py` 53、`test_models.py` 5
|
||||
- **`xfail` 再次清零**:`correlation_matrix` 的契约冲突已按方案 A 解决(决策 41),那条用例转正
|
||||
- 上一轮那 7 条 xfail 已全部转正,决策 30 那套信号(XPASS → 摘 mark)跑完过一个完整闭环
|
||||
@@ -450,21 +467,44 @@ docstring 与 `test_describe_reports_expected_keys` 早就写死了这个方向
|
||||
① `isinstance(bounds, (list, tuple))` 把 `np.array([[0, 1]])` 也拒了,与"尽量传 ndarray"的偏好冲突;
|
||||
② `if` / `else` 两支里 `name` / `params["bounds"]` / `params["dimension"]` 各写了一遍(易错点 20)。
|
||||
|
||||
| 53 | `random_walk` 的默认步长采样器**不能用 `rng.integers(..., dtype=float)`**(numpy 直接抛 `TypeError`);`step_sampler` 收到的 `size` 是**二维** `(n_paths, n_steps)` |
|
||||
用户 2026-10-05 写完第一版。默认采样器原写 `rng.integers(0, 2, size=size, dtype=float) * 2.0 - 1.0` ——
|
||||
`Generator.integers` 不收浮点 dtype(`TypeError: Unsupported dtype dtype('float64') for integers`),
|
||||
于是**整个默认路径 100% 失败**,连 docstring 的 Examples 都跑不通;Codex 按决策 31 改成 `rng.integers(0, 2, size=size) * 2 - 1`
|
||||
(交给后面的 `np.asarray(..., dtype=float)` 转类型),实测形状 `(5, 101)`、步长恒 ±1、同种子可复现。
|
||||
`step_sampler` 的 `size` 是 `(n_paths, n_steps)` 而不是 `monte_carlo` 那样的一个整数 —— 契约已写进 docstring,
|
||||
**遗留 4 条待办**见"下一步"(`TypeError`/`ValueError` 分工、`steps` 形状校验、NaN 口径、`params` 缺 `random_state`)。
|
||||
|
||||
## 下一步
|
||||
|
||||
### ⭐ 当前进度与待办(2026-10-05 第二轮)
|
||||
### ⭐ 当前进度与待办(2026-10-05 第三轮)
|
||||
|
||||
**本轮:`monte_carlo_integral` 已由用户写完并审查通过**(L1,S 档)。Codex 只改了一处**纯 bug**
|
||||
(体积 `lows - highs` → `highs - lows`,见决策 51)—— **算法结构一行未动**。
|
||||
**本轮:`random_walk` 已由用户写完第一版并审查通过**(L1,S 档)。Codex 改了一处**真 bug**
|
||||
(默认采样器 `rng.integers(..., dtype=float)` 是 numpy 不支持的写法,默认路径 100% 失败,见决策 53),
|
||||
另补 ruff / docstring / **6 条从无到有的测试**。
|
||||
|
||||
**接下来三件事**
|
||||
1. `monte_carlo_integral` 的 3 条待办**已办结**;新增 2 条口径(下面一节)——都是你的活。
|
||||
1. `random_walk` 有 **4 条待办**(下面一节)——都是你的活。
|
||||
2. `ci_low` / `ci_high` 的语义确认一句(也是你的活),确认后 Codex 补 `docs/algorithms/monte_carlo.md` 卡片。
|
||||
3. L0 最后一项 `save_figure`(A 档 = 工程,`viz/export.py`)—— **Codex 直接实现**,你只读。
|
||||
|
||||
**你下一个要写的(S 档,仍在 `solve/simulate.py`)**:`random_walk`。
|
||||
它和刚写完的两个函数是**同一套骨架的第三次复用**(造批 → 一次算 → 汇总),新东西只有两处:
|
||||
`np.cumsum` 把增量累成轨迹;`data` 的 shape 从 `(n,)` 变成 `(n_paths, n_steps + 1)` 的**二维**。
|
||||
**你下一个要写的(S 档,仍在 `solve/simulate.py`)**:`markov_simulate` → `markov_steady_state`。
|
||||
两者是同一件事的两面:前者按转移矩阵**逐代抽状态**(`rng.choice` 的 `p=` 版),后者用**迭代**求平稳分布。
|
||||
`markov_steady_state` 的 docstring 已写明口径:用迭代而不用特征分解,因为"收敛快不快"本身就是论文里要写的性质。
|
||||
|
||||
### ⏳ 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` 对比),要么直接去掉。
|
||||
|
||||
### ✅ monte_carlo_probability 的 6 条待办(2026-10-05 全部办结)
|
||||
|
||||
@@ -488,13 +528,15 @@ docstring 与 `test_describe_reports_expected_keys` 早就写死了这个方向
|
||||
`bounds=[0, 1]` 现在报"bounds 必须形如 [(lo, hi), ...],当前形状为 (2,),请检查是否漏了括号"。
|
||||
- ~~③ `n: int`~~ → 已改 `int | np.integer`。
|
||||
|
||||
### ⏳ monte_carlo_integral 还有 2 条口径(新增,你的活)
|
||||
### ✅ monte_carlo_integral 的 2 条新口径(2026-10-05 已办 1 条、剩 1 条)
|
||||
|
||||
1. **`bounds` 把 ndarray 也拒了**:`isinstance(bounds, (list, tuple))` 使 `np.array([[0.0, 1.0]])` 直接 `ValueError`。
|
||||
这和你自己定的口径("以后我也尽量不传 list 和 tuple,尽量用 `pd.DataFrame` / `pd.Series` / `np.array`")冲突 ——
|
||||
要不要放成"list / tuple / ndarray 都收"?(`np.asarray` 本来就能接住三者)
|
||||
2. **`if` / `else` 两个分支里那三行写了两遍**:`result.name`、`params["bounds"]`、`params["dimension"]` 各写一次。
|
||||
要改的话很简单:这三行提到 `if` 之前,只让 `message` 分两支 —— 正是易错点 20 说的"同一个信息写两遍"。
|
||||
- ~~① `bounds` 把 ndarray 拒了~~ → 已加 `isinstance(bounds, np.ndarray)` 分支,`np.array([[0.0, 1.0]])` 现在能收。
|
||||
- ~~② `if` / `else` 里那三行重复~~ → 已把 `name` / `params["bounds"]` / `params["dimension"]` 提到 `if` 之前,只剩 `message` 分两支。
|
||||
|
||||
**还剩 1 条(你的活)**
|
||||
1. **第一道 `isinstance` 分支现在是冗余的**:`np.asarray` 本来就同时收 list / tuple / ndarray,
|
||||
而 `[]`、`"abc"`、标量这些都会被下面那句形状校验抓住(报"当前形状为 (0,)"等)。
|
||||
整段可以塌成两行:`bounds_arr = np.asarray(bounds)` + 形状校验 —— 顺带 `bounds.copy()` 也是多余的一次拷贝。
|
||||
|
||||
### ⏳ monte_carlo 只剩 1 个口径要你确认
|
||||
|
||||
|
||||
Reference in new issue
Block a user