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:
guzhujushi committed 2026-10-09 23:52:53 +08:00
1 parent 0955890010
commit 953e988983
5 files changed
+311 -72

No files matched your search

+33
View File
@@ -555,6 +555,39 @@
`tol=np.nan` 穿过校验白跑迭代、`converged=False` 仍 `success=True`、
类型错也抛 `ValueError`(与决策 54 分工不一致)、`params` 没记转移矩阵。
### Added
- `solve.mm1_metrics` 落地(**L1,S 档**,实现由用户本人写):闭式解 `ρ = λ/μ`、`L = ρ/(1-ρ)`、
`Lq = ρ²/(1-ρ)`、`W = 1/(μ-λ)`、`Wq = W - 1/μ`;`λ >= μ` 走"系统不稳定"分支返回 `success=False`。
Codex 审查结论:**公式全对**(探针实测 3 组参数 `(2,3)` / `(1,4)` / `(0.5,1.0)` 与闭式公式逐位一致,
`W = Wq + 1/μ` 恒等式成立)。至此 `solve` 层的 L1 清单**全部落地**。
- `markov_simulate` / `markov_steady_state` 增加**非负校验**(用户 2026-10-09 改定):含负元素的行随机矩阵
现在入口即报错,不再静默给出错结果(前者会"全落 0",后者会溢出成 `nan` 却报成功)。
- `markov_steady_state` 的 `params` 补 `transition`(`arr.tolist()`);`success=converged`
(未收敛判失败,口径 B);`tol` 拆成 `TypeError` / `ValueError` 且先判 `bool`。
- `tests/test_solve.py`:摘掉 2 条 `mm1_metrics` 的 skip,新增 4 条(含 `λ == μ` 的回归测试);本轮累计 +6 条。
### Fixed
- **`mm1_metrics` 的稳定性分界写错**(真 bug,按决策 31 修):`service_rate < arrival_rate` 与 docstring / 既有测试的
`λ >= μ` 口径不符 —— `λ == μ` 会掉进 `try`、`rho == 1.0` → `ZeroDivisionError`,被吞成 `success=False`
(结果碰巧对,但 message 成了"计算失败:ZeroDivisionError - float division by zero")。改成 `<=` 并加回归测试;
**两个分支的可观测输出完全相同(`success=False` / `metrics={}` / `data=None`),故未动任何口径**。
- **三处报错文案的参数名抄错**:`markov_steady_state` 的 `max_iter <= 0` 写成"tol 必须是正数";
`mm1_metrics` 的 `service_rate` 两条报错都写成 "tol"。顺带把半角逗号统一成全角。
### Changed
- `mm1_metrics` 的 docstring 补 `Raises` 与 `data` 说明;`call_params = {{...}}` 补空格;
ruff 6 条(5 处 `W293` + `F541` f-string 无占位符 + `W292` 文件末尾无换行)。
- `markov_steady_state` 的 `try` 前 3 个空行 → 1;`异常: ` → `异常:`。
- `STATUS.md`:决策 56 / 57 更新办结项,新增**决策 58**(`mm1_metrics` 的 4 条待拍板);「下一步」推进到第七轮;
验证计数更新为 **202 passed / 28 skipped(230 收集)**。
- `docs/dev/personal-pitfalls.md`:新增易错点 29(同一个校验里的两把尺子)。
### Notes
- **待用户拍板的口径**(Codex 只报不改):`markov_simulate` 4 条(`initial` 语义 A/B/C、`paths[:, 0]` 赋值位置、
状态越界、`std_terminal` 对分类量)、`markov_steady_state` 3+2 条(`tol` 的 NaN、`max_iter` 类型错、
非负校验的**容差**、未收敛却带 `data`)、`mm1_metrics` 4 条(`λ = 0`、numpy 标量、`data=metrics`、
失败分支丢 `rho`)。详见 `STATUS.md` 决策 56 / 57 / 58。
## [0.0.1] - 2026-10-01
### Added