fix(solve): 审查 mm1_metrics + 第二批入口改动(工程侧收口 + 6 条测试)
用户本轮改了 markov_simulate / markov_steady_state 的入口校验,并写完 mm1_metrics,
三处算法本体都正确(mm1 的 5 个公式经探针与闭式解逐位对拍通过,含 W = Wq + 1/mu)。
Codex 只动工程侧:
Fixed
- mm1_metrics 的稳定性分界 "<" 应为 "<="(docstring 与既有测试的口径都是 lambda >= mu 不稳定):
lambda == mu 原先掉进 try、rho == 1.0 触发 ZeroDivisionError,被 except 吞成 success=False。
两个分支的可观测输出完全相同(success=False / metrics={} / data=None),只有 message 不同,
故按决策 31 直接修并加回归测试,未改动任何口径。
- 三处报错文案的参数名抄错:markov_steady_state 的 max_iter <= 0 写成 "tol 必须是正数",
mm1_metrics 的 service_rate 两条报错都写成 "tol";半角逗号统一为全角。
Changed
- mm1_metrics 的 docstring 补 Raises 与 data 说明;call_params = {...} 补空格;
ruff 6 条(W293 x5 / F541 f-string 无占位符 / W292 文件末尾无换行)。
- markov_steady_state 的 try 前 3 个空行 -> 1;"异常: " -> "异常:"。
- tests/test_solve.py:摘掉 2 条 mm1_metrics 的 skip,新增 4 条(lambda == mu 的回归、
非负校验回归、max_iter=3 未收敛判 success=False)。
- STATUS.md 决策 56 / 57 更新办结项 + 新增决策 58(mm1 的 4 条待拍板);CHANGELOG;易错点 29。
校验:pytest 202 passed / 28 skipped(230 收集);ruff 0;pyright 0
This commit is contained in:
1 parent
0955890010
commit
953e988983
5 files changed
+311
-72
No files matched your search
@@ -22,9 +22,11 @@
|
||||
`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`、`random_walk`、`markov_simulate`、
|
||||
`markov_steady_state`(**均为用户写**)。剩余 1 项:`save_figure`(Codex 写)。
|
||||
`markov_steady_state`、`mm1_metrics`(**均为用户写**)—— `solve` 层的 L1 清单**已全部落地**。
|
||||
剩余 1 项:`save_figure`(Codex 写)。
|
||||
`monte_carlo` 的口径已拍板(决策 48);`monte_carlo_probability` / `random_walk` 的待办**均已办结**;
|
||||
`markov_simulate`(决策 56,5 条)与 `markov_steady_state`(决策 57,6 条)的**入口口径待你拍板**。
|
||||
`markov_simulate`(决策 56)/ `markov_steady_state`(决策 57)/ `mm1_metrics`(决策 58)
|
||||
**仍有一批入口口径待你拍板**(本轮各办结了一批,详见"下一步")。
|
||||
另 3 个小口径仍悬着:`ci_low`/`ci_high` 的语义、`monte_carlo_integral` 的第一道 `isinstance` 分支可塌成两行、
|
||||
`random_state` 注解与 `std` 的 `ddof` 是否全项目对齐。
|
||||
|
||||
@@ -333,7 +335,9 @@
|
||||
单位矩阵吸收态自锁、可复现(同种子相同、异种子不同)、终点频率收敛到 `[2/3, 1/3]`、
|
||||
metrics = 终点状态、非法转移矩阵(非方阵 / 行和不为 1)、非法 `steps` / `n_paths`(0 / float / bool)、
|
||||
`np.int64` 可收、docstring Example 真能跑
|
||||
- ⏳ **留给你的 5 条**见"下一步 → markov_simulate 待你拍板"(全是入口口径与少考虑情况,决策 49)
|
||||
- **第二轮(2026-10-09)**:用户补了 `np.any(arr < 0)` 非负校验 → 待办 ② 办结;
|
||||
Codex 补一条"行和是 1 但含负元素"的回归测试
|
||||
- ⏳ **余下 4 条**见"下一步 → markov_simulate 待你拍板"(全是入口口径与少考虑情况,决策 49)
|
||||
|
||||
- [x] `solve/simulate.py` 的 `markov_steady_state`(**L1,S 档,算法由用户本人写**,2026-10-09)
|
||||
- 核心:`v` 从均匀分布出发,`v_next = v @ arr` 反复推进(行随机矩阵按**行向量右乘**),
|
||||
@@ -349,7 +353,28 @@
|
||||
- **测试:+8 条、摘掉 1 条 skip**(此前该函数只有 1 条挂着的 skip)—— 2 状态 `[2/3, 1/3]` + `converged` / `iterations`、
|
||||
3 状态与特征分解对拍、`data` 是分布(和为 1 / 非负 / `float64`)、慢混合链迭代次数 > 100、
|
||||
`np.int64` 的 `max_iter`、非法转移矩阵、`max_iter=0` / `tol=0`、docstring Example 真能跑
|
||||
- ⏳ **留给你的 6 条口径**见"下一步 → markov_steady_state 待你拍板"(决策 57)
|
||||
- **第二轮(2026-10-09,用户改定)**:补非负校验(① 办结)、`params` 记 `transition`(⑥ 办结)、
|
||||
`success=converged`(选口径 B,④ 办结)、`tol` 拆成 `TypeError` / `ValueError` 且先判 `bool`(② 办结);
|
||||
Codex 顺手修文案(`max_iter <= 0` 那条写成 "tol 必须是正数")与 `异常: ` → `异常:`,
|
||||
并补 2 条测试(负元素矩阵、`max_iter=3` 未收敛走 `success=False`)
|
||||
- ⏳ **余下 3 条 + 1 个新口子**见"下一步 → markov_steady_state 待你拍板"(决策 57)
|
||||
|
||||
- [x] `solve/simulate.py` 的 `mm1_metrics`(**L1,S 档,算法由用户本人写**,2026-10-09)
|
||||
- 核心:闭式解 `ρ = λ/μ`、`L = ρ/(1-ρ)`、`Lq = ρ²/(1-ρ)`、`W = 1/(μ-λ)`、`Wq = W - 1/μ`;
|
||||
`λ >= μ` 走"系统不稳定"分支返回 `success=False`
|
||||
- **Codex 审查结论:公式全对**(探针实测 3 组参数 `(2,3)` / `(1,4)` / `(0.5,1.0)` 与闭式公式逐位一致,
|
||||
`W = Wq + 1/μ` 恒等式也成立)
|
||||
- **Codex 修了一处边界 bug(决策 31,纯 bug)**:稳定性判断写成 `service_rate < arrival_rate`,
|
||||
而 docstring 与既有测试的口径都是 `λ >= μ` —— 于是 `λ == μ` 掉进 `try`、`rho == 1.0` →
|
||||
`ZeroDivisionError` 被 `except` 吞成 `success=False`(结果碰巧对,message 却是"计算失败:ZeroDivisionError")。
|
||||
两支的可观测输出完全相同(`success=False` / `metrics={}` / `data=None`),改 `<=` 只换 message,故直接修并加回归测试
|
||||
- **Codex 只碰工程侧**:① ruff 6 条(5 处 `W293` + `F541` f-string 无占位符 + `W292` 文件末尾无换行);
|
||||
② 文案:`service_rate` 的报错写成 "tol"、`max_iter` 的也写成 "tol"(同一轮里 3 处参数名抄错)、半角逗号 → 全角;
|
||||
③ `call_params={...}` → `call_params = {...}`;④ docstring 补 `Raises` + 说明 `data` 与 `metrics` 同值
|
||||
- **测试:+6 条、摘掉 2 条 skip**(此前只有 2 条挂着的 skip)—— 闭式公式逐项对拍(含 `W = Wq + 1/μ`)、
|
||||
`λ == μ` 与 `λ > μ` 都判失败且带"不稳定"文案、入口(0 / 负数 / `bool` / 非数值)、
|
||||
`λ == μ` 不能靠 `ZeroDivisionError` 落到失败分支、docstring Example
|
||||
- ⏳ **留给你的 4 条**见"下一步 → mm1_metrics 待你拍板"(决策 58)
|
||||
|
||||
- [ ] 剩余 1 项:`save_figure`(A 档 = 工程,**Codex 直接写**)
|
||||
|
||||
@@ -357,7 +382,7 @@
|
||||
|
||||
| 检查 | venv | conda `mcm-toolkit` |
|
||||
|---|---|---|
|
||||
| `pytest` | 见下(单列) | **196 passed, 30 skipped(0 xfail)** |
|
||||
| `pytest` | 见下(单列) | **202 passed, 28 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` | 跑通 | — |
|
||||
@@ -367,15 +392,15 @@
|
||||
(修配置之前是 17 errors,全是"无法解析导入 numpy / pandas",见决策 29 与
|
||||
`docs/04-environment.md` 坑 7)
|
||||
- base 亦已复核:numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6(与两套环境一致)
|
||||
- **226 条用例:196 passed / 30 skipped / 0 xfail**。收集数按文件:`test_data.py` 52、`test_api_surface.py` 20、
|
||||
`test_core.py` 23、`test_solve.py` 58(本轮 +8、摘掉 1 条 skip)、`test_viz.py` 8、`test_decision.py` 7、
|
||||
- **230 条用例:202 passed / 28 skipped / 0 xfail**。收集数按文件:`test_data.py` 52、`test_api_surface.py` 20、
|
||||
`test_core.py` 23、`test_solve.py` 62(本轮 +4、再摘掉 2 条 skip)、`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)跑完过一个完整闭环
|
||||
- `examples/01_clean_and_describe.py` 实测跑通(exit 0);`00_quickstart.py` 仍等 `stats` / `viz`
|
||||
- 30 条 skip 是等实现的算法(`train_test_split`、`mm1_metrics`、`anova`、`fit_distribution`、
|
||||
`knapsack_dp`、`linear_program`、`simulated_annealing` 等);
|
||||
`monte_carlo_probability` / `monte_carlo_integral` / `markov_steady_state` 已落地,分别摘掉 2 / 1 / 1 条 skip;`markov_simulate` 新增的 9 条全部实跑;
|
||||
- 28 条 skip 是等实现的算法(`train_test_split`、`anova`、`fit_distribution`、`knapsack_dp`、
|
||||
`linear_program`、`simulated_annealing` 等);
|
||||
`monte_carlo_probability` / `monte_carlo_integral` / `markov_steady_state` / `mm1_metrics` 已落地,分别摘掉 2 / 1 / 1 / 2 条 skip;`markov_simulate` 新增的 9 条全部实跑;
|
||||
`TestValidate` 的 4 条仍挂着 —— `check_*` 还是 `NotImplementedError`,但它们实现时应当直接复用 `as_array`
|
||||
- **本轮回归(已修)**:`transform.py` 上一版有 3 处真 bug —— 修前 **7 failed / 75 passed**,
|
||||
且 pyright 在同一处报 `reportArgumentType`。**不是静态误报**:pyright CLI 复现 + 测试真红。
|
||||
@@ -541,34 +566,47 @@ docstring 与 `test_describe_reports_expected_keys` 早就写死了这个方向
|
||||
长度不匹配时**裸抛** `ValueError`(本该 `success=False`,同易错点 26 一族)。另有三个口子:
|
||||
`np.int64(0)` 掉进数组分支、被 `as_array` 拒(与 `steps`/`n_paths` 收 `np.integer` 不一致,易错点 1 复发);
|
||||
`True` 因 `isinstance(True, int)` 为真被当状态 1 **静默放行**;越界状态(如 `initial=5`、`s=2`)只在
|
||||
第一轮索引时才炸、被 `except` 吞成 `success=False`。可选口径见下一步的 A / B / C。
|
||||
第一轮索引时才炸、被 `except` 吞成 `success=False`。
|
||||
**2026-10-09 更新**:② 已办结(用户补了 `np.any(arr < 0)`,含负元素的行随机矩阵现在入口直接报错);
|
||||
① ③ ④ ⑤ 仍待拍板(A / B / C 三个选项见"下一步")。
|
||||
|
||||
| 57 | `markov_steady_state` 的 6 条口径**待用户拍板**(Codex 只报选项,未代改,决策 49) |
|
||||
探针实测出的口子:① **含负元素**的"行和为 1"矩阵(`[[1.5, -0.5], [0, 1]]`)会溢出成 `nan`,而
|
||||
`data=[nan, nan]` 却 `success=True` —— 违反决策 48 的 NaN 口径(根因:没查 `arr >= 0`);附带的
|
||||
`RuntimeWarning: overflow encountered in matmul` 因为 pyproject 设了 `filterwarnings = ["error::RuntimeWarning"]`,
|
||||
**能让测试直接报错**,所以不能只靠测试兜;② `tol=True` 因 `isinstance(True, int)` 通过校验 → `tol=1` →
|
||||
1 次就"收敛",给出 `[0.55, 0.45]` 的假答案(`max_iter` 查了 `bool`、`tol` 没查,易错点 1 复发);
|
||||
③ `tol=np.nan` 因 `nan <= 0` 为假穿过校验 → 白跑 10000 次迭代;④ `converged=False` 仍 `success=True`
|
||||
且 `data` 是未收敛的迭代值(`max_iter=5` → `[0.6387, 0.3613]`);⑤ 类型错也抛 `ValueError`
|
||||
(`max_iter=100.0`),与决策 54 的"类型错 `TypeError`"分工不一致;⑥ `params` 只记 `max_iter` / `tol`,
|
||||
没记转移矩阵(`monte_carlo_integral` 记了 `bounds`),事后复现不了。可选口径见"下一步"。
|
||||
| 57 | `markov_steady_state` 的口径:**4 条已办、3 条 + 2 个新口子仍待拍板**(Codex 只报选项,未代改,决策 49) |
|
||||
**2026-10-09 用户第二轮办结**:① 补 `np.any(arr < 0)`(含负元素的行随机矩阵现在入口即报错,原先会溢出成 `nan`
|
||||
却报 `success=True`);② `tol` 拆成 `TypeError`(类型 + `bool`)与 `ValueError`(取值);
|
||||
④ **选口径 B**:`converged=False` → `success=False`;⑥ `params` 补 `transition`(`arr.tolist()`,可 JSON 化)。
|
||||
**仍待拍板**:③ `tol=np.nan`(`nan <= 0` 为假,穿过校验白跑 §max_iter 次迭代);
|
||||
⑤ `max_iter` 的类型错仍抛 `ValueError`(与决策 54 的"类型错 `TypeError`"不一致);
|
||||
⑦ **两把尺子宽严不一**——非负校验用精确 `arr < 0`,同一个函数里的行和校验却用宽容的 `np.allclose`,
|
||||
实测 `[[1.0, -1e-16], [0.5, 0.5]]`(行和都是 1)会被判非法,建议改成 `< -1e-12` 这类容差;
|
||||
⑧ 未收敛时 `success=False` 但 `data` 仍是未收敛向量(文件内唯一"失败却带 `data`"的分支)—— 保持还是 `data=None`?
|
||||
|
||||
| 58 | `mm1_metrics` 的稳定性分界由 Codex 按**决策 31** 直接修(`<` → `<=`),另有 4 条口径待用户拍板 |
|
||||
docstring 与既有测试写的都是 `λ >= μ` 不稳定,代码却写 `service_rate < arrival_rate` —— 于是 `λ == μ`
|
||||
掉进 `try`、`rho == 1.0` → `ZeroDivisionError`,被 `except` 吞成 `success=False`
|
||||
(碰巧对,但 message 成了"计算失败:ZeroDivisionError - float division by zero")。两支的可观测输出完全相同
|
||||
(`success=False` / `metrics={}` / `data=None`),改 `<=` 只换 message,故直接修 + 加回归测试。
|
||||
**待拍板**:① `λ = 0` 现在被 `ValueError` 拒(数学上是合法退化解:空系统 `L = W = 0`);
|
||||
② `np.int64` / `np.float32` 全被 `TypeError` 拒(文件内 `random_walk` 的 `start` 收 `np.number`、
|
||||
整数参数收 `np.integer`,§4.7 有共识);③ `data=metrics` —— **同一个 dict 对象**(`r.data is r.metrics`),
|
||||
且成功分支给 dict、失败分支给 `None`,与文件内"`data` 是数组"的习惯不同;
|
||||
④ 失败分支的 `metrics={}` 会丢掉 `rho` 等已算出的量(可选保留)。
|
||||
|
||||
## 下一步
|
||||
|
||||
### ⭐ 当前进度与待办(2026-10-09 第六轮)
|
||||
### ⭐ 当前进度与待办(2026-10-09 第七轮)
|
||||
|
||||
**本轮:`markov_steady_state`(你写的)已审查通过** —— 算法本体正确,一行没动(3 状态与特征分解逐位一致、
|
||||
200×200 只用 6 次迭代 / 0.10s);Codex 只清工程侧(ruff 5 条、docstring 补 `max_iter`/`tol`/`Raises`、
|
||||
修正"周期链"表述、全角冒号),并 **+8 条测试、摘掉 1 条 skip**。
|
||||
三连:`pytest` **196 passed / 30 skipped**、`ruff` 0、`pyright` 0。
|
||||
**本轮:你改了 `markov_simulate` / `markov_steady_state` 的入口,并写完 `mm1_metrics`** —— 三处算法本体都正确。
|
||||
Codex 只做工程侧:`mm1_metrics` 修了一处**边界 bug**(`<` → `<=`,见决策 58)、ruff 6 条、4 处文案参数名抄错、
|
||||
docstring 补 `Raises`、**+6 条测试 + 摘掉 2 条 skip**。
|
||||
三连:`pytest` **202 passed / 28 skipped**、`ruff` 0、`pyright` 0。`solve` 层 L1 清单**已全部落地**。
|
||||
|
||||
**接下来三件事**
|
||||
1. **`markov_steady_state` 的 6 条口径待你拍板**(下节,决策 57)。
|
||||
2. **`markov_simulate` 的 5 条口径**仍在挂着(决策 56 的 A / B / C),与 `solve` 的收尾一起定。
|
||||
3. L0 最后一项 `save_figure`(A 档 = 工程,`viz/export.py`)—— **Codex 直接实现**,你只读。
|
||||
1. **三个函数的口径待你拍板**:`markov_simulate` 4 条(决策 56)、`markov_steady_state` 3+2 条(决策 57)、
|
||||
`mm1_metrics` 4 条(决策 58)—— 全在下面三节。
|
||||
2. L0 最后一项 `save_figure`(A 档 = 工程,`viz/export.py`)—— **Codex 直接实现**,你只读。
|
||||
3. 之后就往外层走:L1 只剩 `decision` / `models` / `stats` 的几条。
|
||||
|
||||
### ⏳ markov_simulate 待你拍板(5 条,Codex 只报不改)
|
||||
### ⏳ markov_simulate 待你拍板(4 条)
|
||||
|
||||
**① `initial` 的语义(最大的一条,三个选项)**
|
||||
现在标量分支按**路径数**铺满、数组分支按**状态数**校验长度,两条口径互相矛盾(详见决策 56)。
|
||||
@@ -576,39 +614,57 @@ docstring 与 `test_describe_reports_expected_keys` 早就写死了这个方向
|
||||
- **B**:只收长度 `n_paths` 的数组(标量走 `np.full` 铺满),`data` 首列就是给定的初始状态序列(与 `paths[:, 0]` 赋值天然一致)
|
||||
- **C**:沿用现状,但把类型收成 `int | np.integer` + 先判 `bool`,把 `paths[:, 0] = ...` 挪进 `try`,docstring 写清"数组按路径数"
|
||||
|
||||
**② 转移矩阵没查非负**:`[[1.5, -0.5], [0, 1]]` 行和恰为 1.0 → 通过校验,但 `cumsum` 非单调 →
|
||||
落格结果**全是 0**(静默错)。建议入口加一条 `np.any(arr < 0)` → `ValueError`。
|
||||
~~**② 转移矩阵没查非负**~~ → **已办(2026-10-09)**:入口加了 `np.any(arr < 0)` → `ValueError`,
|
||||
测试里钉了"行和是 1 但含负元素"这条。余下的一小问:这条校验是**精确**判负,见决策 57 的 ⑦(容差)。
|
||||
|
||||
**③ `paths[:, 0]` 赋值在 `try` 之外**:长度不匹配时裸抛 `ValueError`,与"出错统一 `success=False`"(决策 48)不一致。
|
||||
|
||||
**④ 状态越界没在入口拦**:`initial=5`(`s=2`)第一轮才 `IndexError`,被吞成 `success=False`;
|
||||
建议入口加 `0 <= initial < s`。
|
||||
**⑤ `std_terminal` 对**分类**状态的意义**:状态编号是标称量,均值和标准差只对"有序状态"(如排队人数)成立 ——
|
||||
|
||||
**⑤ `std_terminal` 对分类状态的意义**:状态编号是标称量,均值和标准差只对"有序状态"(如排队人数)成立 ——
|
||||
要不要把 `metrics` 换成状态频率(`terminal_freq`)?你定。
|
||||
|
||||
### ⏳ markov_steady_state 待你拍板(6 条,Codex 只报不改)
|
||||
### ⏳ markov_steady_state 待你拍板(3 条 + 2 个新口子)
|
||||
|
||||
**① 转移矩阵没查非负**(与 `markov_simulate` 的待办 ② 是同一块):`[[1.5, -0.5], [0, 1]]` 行和恰为 1.0 →
|
||||
通过校验 → `v` 指数发散 → `data=[nan, nan]` **但 `success=True`**(违反决策 48 的 NaN 口径)。
|
||||
实测还会先抛 `RuntimeWarning: overflow encountered in matmul`,而 pyproject 设了
|
||||
`filterwarnings = ["error::RuntimeWarning"]` —— 这条**能让测试直接报错**,所以得在校验层拦,不能只靠测试兜。
|
||||
- ~~**① 转移矩阵没查非负**~~ → **已办(2026-10-09)**
|
||||
- ~~**② `tol` 没查 `bool`**~~ → **已办**:`tol` 现在拆成 `TypeError`(类型 / `bool`)与 `ValueError`(取值)
|
||||
- ~~**④ `converged=False` 仍 `success=True`**~~ → **已办**:选了**口径 B**,`success=converged`
|
||||
- ~~**⑥ `params` 没记转移矩阵**~~ → **已办**:补了 `"transition": arr.tolist()`
|
||||
|
||||
**② `tol` 没查 `bool`**:`tol=True` → `isinstance(True, int)` 为真 → `tol=1` →
|
||||
1 次就"收敛",给出 `[0.55, 0.45]` 的**假答案**(`success=True, converged=True, iterations=1`)。
|
||||
`max_iter` 查了 `bool`,`tol` 漏了(易错点 1 复发)。
|
||||
**③ `tol` 没查 NaN**:`np.nan <= 0` 为假 → 穿过校验 → 白跑满 10000 次迭代才返回(结果本身对,纯浪费)。
|
||||
建议与 `max_iter` 一起走"类型 → 有限性 → 取值"三件套(易错点 28)。
|
||||
|
||||
**③ `tol` 没查 NaN**:`np.nan <= 0` 为假 → 穿过校验 → 白跑 10000 次迭代后返回(结果本身是对的,纯浪费)。
|
||||
**⑤ `max_iter` 的类型错仍抛 `ValueError`**(`max_iter=100.0` / `max_iter=True`),
|
||||
与文件内其它函数(决策 54:类型错 `TypeError`、取值错 `ValueError`)不一致 —— 要不要对齐?
|
||||
|
||||
**④ `converged=False` 仍 `success=True`**,且 `data` 是未收敛的迭代值(`max_iter=5` → `[0.6387, 0.3613]`)。
|
||||
选项:**A** 保持现状(`converged` 自己看);**B** 不收敛判 `success=False`;**C** 不收敛时 `data=None`、只留 `metrics`。
|
||||
**⑦【新】非负校验的两把尺子**:`arr < 0` 是**精确**判断,而同一个函数里的行和校验用的是宽容的 `np.allclose`。
|
||||
实测 `[[1.0, -1e-16], [0.5, 0.5]]`(行和都是 1.0,只是浮点归一化的残渣)会被判"含负元素"而报错。
|
||||
建模时转移矩阵常常是从数据归一化出来的,这种 `-1e-17` 级残渣很常见 —— 建议改成 `np.any(arr < -1e-12)`
|
||||
(或 `arr.min() < -1e-12`)这样的容差写法。`markov_simulate` 的同名校验也适用。
|
||||
|
||||
**⑤ 类型错也抛 `ValueError`**(`max_iter=100.0`),与文件内其它函数(决策 54:类型错 `TypeError`、取值错 `ValueError`)
|
||||
不一致 —— 要不要对齐?`tol` 同理。
|
||||
**⑧【新】未收敛时 `success=False` 但 `data` 仍是未收敛向量**:这是文件里唯一"失败却带 `data`"的分支
|
||||
(其它失败分支一律 `data=None`)。调用方若只看 `data is not None` 就会拿到没收敛的结果 —— 保持(B)还是改 `data=None`(C)?
|
||||
|
||||
**⑥ `params` 没记转移矩阵**:`{"max_iter": 10000, "tol": 1e-10}` 复现不了"算的是哪个矩阵"
|
||||
(`monte_carlo_integral` 记了 `bounds`)—— 要不要补?
|
||||
### ⏳ mm1_metrics 待你拍板(4 条)
|
||||
|
||||
**你下一个要写的(S 档,仍在 `solve/simulate.py`)**:`mm1_metrics`(M/M/1 闭式指标,10 行)。
|
||||
docstring 已写明口径:`λ >= μ` 时系统不稳定、队列长度无界 —— 这种情况返回 `success=False` 并说明原因,
|
||||
而不是返回 `inf`。写完 `solve` 这一层就齐了,L1 只剩 `decision` / `models` 那几条。
|
||||
**① `λ = 0` 被拒**:现在 `arrival_rate <= 0` → `ValueError`。数学上 `λ = 0` 是合法退化解
|
||||
(没有顾客到达,`ρ = L = Lq = W = Wq = 0`)—— 要收还是继续拒?
|
||||
|
||||
**② numpy 标量全被拒**:`np.int64(2)` / `np.float32(2.0)` → `TypeError`。
|
||||
文件内其它函数已按 §4.7 放宽(`random_walk` 的 `start` 收 `np.number`、计数参数收 `np.integer`),
|
||||
这里还是 `(int, float)` —— 要不要对齐?(`np.float64` 本来就是 `float` 的子类,不受影响)
|
||||
|
||||
**③ `data=metrics`**:`data` 和 `metrics` 是**同一个 dict 对象**(实测 `r.data is r.metrics` 为 `True`),
|
||||
而且成功分支给 dict、失败分支给 `None`,与文件内"`data` 是样本数组"的习惯不同。选项:**A** 保持;
|
||||
**B** `data=None`(闭式解没有样本数组);**C** 只留 `metrics`、`data` 给一个 5 元素数组。
|
||||
|
||||
**④ 失败分支丢掉了已算出的量**:`λ >= μ` 时 `metrics={}`,但 `ρ = λ/μ` 其实算得出来
|
||||
(`ρ >= 1` 本身就是"不稳定"的量化说明,论文里常要写)—— 要不要在失败分支里保留 `rho`?
|
||||
|
||||
**你下一个要写的**:`solve` 这层已经齐了(`mm1_metrics` 落地后 L1 清单走完)。
|
||||
建议下一步挑 **`decision` / `models` 里你感兴趣的 L1 函数**开刀(E 节有清单),
|
||||
或者先把上面三节的口径一次性拍完、Codex 批量收口 —— 两条路都行,你定。
|
||||
|
||||
### ✅ random_walk 的 4 条待办(2026-10-05 已办结)
|
||||
|
||||
|
||||
Reference in new issue
Block a user