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
@@ -521,6 +521,8 @@ def markov_simulate(
|
||||
arr, _ = as_array(transition)
|
||||
if arr.ndim != 2 or arr.shape[0] != arr.shape[1]:
|
||||
raise ValueError(f"transition 必须是二维方阵,你传入的形状是 {arr.shape}")
|
||||
if np.any(arr < 0):
|
||||
raise ValueError("transition 矩阵的元素必须是非负的(概率不能为负)")
|
||||
if not np.allclose(arr.sum(axis=1), 1.0):
|
||||
raise ValueError("transition 每一行的和必须是 1.0")
|
||||
s = arr.shape[0]
|
||||
@@ -638,19 +640,30 @@ def markov_steady_state(transition: Any, max_iter: int | np.integer = 10000, tol
|
||||
arr, _ = as_array(transition)
|
||||
if arr.ndim != 2 or arr.shape[0] != arr.shape[1]:
|
||||
raise ValueError(f"transition 必须是二维方阵,你传入的形状是 {arr.shape}")
|
||||
if np.any(arr < 0):
|
||||
raise ValueError("transition 矩阵的元素必须是非负的(概率不能为负)")
|
||||
if not np.allclose(arr.sum(axis=1), 1.0):
|
||||
raise ValueError("transition 每一行的和必须是 1.0")
|
||||
|
||||
if isinstance(max_iter, bool) or not isinstance(max_iter, (int, np.integer)):
|
||||
raise ValueError(f"max_iter 必须是正整数,你传入了 {type(max_iter).__name__}")
|
||||
if max_iter <= 0:
|
||||
raise ValueError(f"max_iter 必须是正整数,你传入了 {max_iter}")
|
||||
if not isinstance(tol, (int, float)) or isinstance(tol, bool):
|
||||
raise TypeError(f"tol 必须是浮点数,你传入了 {type(tol).__name__}")
|
||||
if tol <= 0:
|
||||
raise ValueError(f"tol 必须是正数,你传入了 {tol}")
|
||||
|
||||
s = arr.shape[0]
|
||||
v = np.full(s, 1.0 / s)
|
||||
|
||||
iterations = 0
|
||||
converged = False
|
||||
|
||||
if isinstance(max_iter, bool) or not isinstance(max_iter, (int, np.integer)) or max_iter <= 0:
|
||||
raise ValueError(f"max_iter 必须是正整数,你传入了 {max_iter}")
|
||||
if not isinstance(tol, (int, float)) or tol <= 0:
|
||||
raise ValueError(f"tol 必须是正数,你传入了 {tol}")
|
||||
call_params = {
|
||||
"transition": arr.tolist(),
|
||||
"max_iter": max_iter,
|
||||
"tol": tol,
|
||||
}
|
||||
|
||||
try:
|
||||
for _ in range(max_iter):
|
||||
@@ -670,21 +683,20 @@ def markov_steady_state(transition: Any, max_iter: int | np.integer = 10000, tol
|
||||
|
||||
return Result(
|
||||
name="markov_steady_state",
|
||||
params={"max_iter": max_iter, "tol": tol},
|
||||
params=call_params,
|
||||
metrics=metrics,
|
||||
data=v,
|
||||
success=True,
|
||||
message=f"平稳分布求解{'成功' if converged else '未完全收敛'},迭代 {iterations} 次。"
|
||||
success=converged,
|
||||
message=f"平稳分布求解{'成功' if converged else '失败:未在最大迭代次数内收敛'},迭代 {iterations} 次。"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return Result(
|
||||
name="markov_steady_state",
|
||||
params={"max_iter": max_iter, "tol": tol},
|
||||
params=call_params,
|
||||
metrics={},
|
||||
data=None,
|
||||
success=False,
|
||||
message=f"平稳分布求解失败:{type(e).__name__} - {str(e)}"
|
||||
message=f"平稳分布求解异常:{type(e).__name__} - {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@@ -700,7 +712,15 @@ def mm1_metrics(arrival_rate: float, service_rate: float) -> Any:
|
||||
Returns
|
||||
-------
|
||||
`Result`:`metrics={"rho", "L", "Lq", "W", "Wq"}`
|
||||
(系统利用率、系统内平均顾客数、队列长度、平均逗留时间、平均等待时间)。
|
||||
(系统利用率、系统内平均顾客数、队列长度、平均逗留时间、平均等待时间);
|
||||
`data` 与 `metrics` 同值(闭式解没有样本数组)。
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError:
|
||||
`arrival_rate` / `service_rate` 不是数值(`bool` 也判非法)。
|
||||
ValueError:
|
||||
`arrival_rate` / `service_rate` 不是正数。
|
||||
|
||||
Notes
|
||||
-----
|
||||
@@ -714,5 +734,58 @@ def mm1_metrics(arrival_rate: float, service_rate: float) -> Any:
|
||||
>>> round(r.metrics["rho"], 4)
|
||||
0.6667
|
||||
"""
|
||||
# TODO(L1)
|
||||
raise NotImplementedError
|
||||
if not isinstance(arrival_rate, (int, float)) or isinstance(arrival_rate, bool):
|
||||
raise TypeError(f"arrival_rate 必须是浮点数,你传入了 {type(arrival_rate).__name__}")
|
||||
if arrival_rate <= 0:
|
||||
raise ValueError(f"arrival_rate 必须是正数,你传入了 {arrival_rate}")
|
||||
if not isinstance(service_rate, (int, float)) or isinstance(service_rate, bool):
|
||||
raise TypeError(f"service_rate 必须是浮点数,你传入了 {type(service_rate).__name__}")
|
||||
if service_rate <= 0:
|
||||
raise ValueError(f"service_rate 必须是正数,你传入了 {service_rate}")
|
||||
|
||||
call_params = {"arrival_rate": arrival_rate, "service_rate": service_rate}
|
||||
|
||||
if service_rate <= arrival_rate:
|
||||
return Result(
|
||||
name="mm1_metrics",
|
||||
params=call_params,
|
||||
metrics={},
|
||||
data=None,
|
||||
success=False,
|
||||
message="系统不稳定:到达率 λ 大于等于服务率 μ,队列长度无界,无法计算闭式解。"
|
||||
)
|
||||
|
||||
try:
|
||||
rho = arrival_rate / service_rate
|
||||
L = rho / (1.0 - rho) # 系统内平均顾客数
|
||||
Lq = rho ** 2 / (1.0 - rho) # 队列中平均等待顾客数
|
||||
W = 1.0 / (service_rate - arrival_rate) # 平均逗留时间
|
||||
Wq = W - 1.0 / service_rate # 平均等待时间
|
||||
|
||||
# 4. 封装返回
|
||||
metrics = {
|
||||
"rho": float(rho),
|
||||
"L": float(L),
|
||||
"Lq": float(Lq),
|
||||
"W": float(W),
|
||||
"Wq": float(Wq)
|
||||
}
|
||||
|
||||
return Result(
|
||||
name="mm1_metrics",
|
||||
params=call_params,
|
||||
metrics=metrics,
|
||||
data=metrics,
|
||||
success=True,
|
||||
message="M/M/1 排队系统指标计算成功。"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return Result(
|
||||
name="mm1_metrics",
|
||||
params=call_params,
|
||||
metrics={},
|
||||
data=None,
|
||||
success=False,
|
||||
message=f"计算失败:{type(e).__name__} - {str(e)}"
|
||||
)
|
||||
Reference in new issue
Block a user