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
@@ -286,10 +286,13 @@ def monte_carlo_integral(
|
||||
>>> abs(r.metrics["mean"] - 1 / 3) < 0.01 # ∫₀¹ x² dx = 1/3
|
||||
True
|
||||
"""
|
||||
if not isinstance(bounds, (list, tuple)) or len(bounds) == 0:
|
||||
raise ValueError("bounds 必须是非空的列表或元组")
|
||||
if not isinstance(bounds, np.ndarray):
|
||||
if not isinstance(bounds, (list, tuple)) or len(bounds) == 0:
|
||||
raise ValueError("bounds 必须是非空的列表或元组")
|
||||
|
||||
bounds_arr = np.asarray(bounds)
|
||||
bounds_arr = np.asarray(bounds)
|
||||
else:
|
||||
bounds_arr = bounds.copy()
|
||||
|
||||
if bounds_arr.ndim != 2 or bounds_arr.shape[1] != 2:
|
||||
raise ValueError(f"bounds 必须形如 [(lo, hi), ...],当前形状为 {bounds_arr.shape},请检查是否漏了括号")
|
||||
@@ -320,15 +323,12 @@ def monte_carlo_integral(
|
||||
random_state=random_state,
|
||||
)
|
||||
|
||||
result.name = "monte_carlo_integral"
|
||||
result.params["bounds"] = bounds
|
||||
result.params["dimension"] = d
|
||||
if result.success:
|
||||
result.name = "monte_carlo_integral"
|
||||
result.params["bounds"] = bounds
|
||||
result.params["dimension"] = d
|
||||
result.message = f"蒙特卡洛多重积分成功:维度 {d},体积 {volume:.4f},模拟 {n} 次。"
|
||||
else:
|
||||
result.name = "monte_carlo_integral"
|
||||
result.params["bounds"] = bounds
|
||||
result.params["dimension"] = d
|
||||
result.message = f"蒙特卡洛多重积分失败:维度 {d},体积 {volume:.4f},模拟 {n} 次。{result.message}"
|
||||
|
||||
return result
|
||||
@@ -341,25 +341,91 @@ def random_walk(
|
||||
start: float = 0.0,
|
||||
random_state: int | None = None,
|
||||
) -> Any:
|
||||
"""随机游走轨迹。
|
||||
"""随机游走轨迹:一次抽完整批增量,再 `cumsum` 成路径。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_steps:
|
||||
每条路径的步数,正整数。`data` 的列数是 `n_steps + 1`(多出的那一列是起点)。
|
||||
n_paths:
|
||||
路径条数,正整数,默认 1 条。
|
||||
step_sampler:
|
||||
`f(rng, size) -> 增量`,默认为 ±1 等概率(对称随机游走)。
|
||||
`f(rng, size) -> 一批增量`。批量大小由本函数以 `size=(n_paths, n_steps)` 传入,
|
||||
必须返回形状 `(n_paths, n_steps)` 的数组(契约同 `monte_carlo` 的 `sampler`,只是这里是二维)。
|
||||
默认 ±1 等概率(对称随机游走)。
|
||||
start:
|
||||
起点,写进每条路径的第 0 列。
|
||||
random_state:
|
||||
随机种子。`None` 时跟随 `core.set_seed` 的全局流,见 `core.random.get_rng`。
|
||||
|
||||
Returns
|
||||
-------
|
||||
`Result`:`data` 形状 `(n_paths, n_steps + 1)`。
|
||||
`Result`:`data` 形状 `(n_paths, n_steps + 1)`,第 0 列恒为 `start`,之后是逐列累加出来的位置;
|
||||
`metrics={"n_steps", "n_paths"}`。
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError:
|
||||
`n_steps` / `n_paths` 不是正整数。
|
||||
TypeError:
|
||||
`start` 不是数值。
|
||||
|
||||
Notes
|
||||
-----
|
||||
随机游走是"平方根扩散"的原型:位置的标准差随步数按 `√t` 增长,所以典型图形是 `±√t` 的喇叭口。
|
||||
本函数只负责 `cumsum`;**建模差异全在 `step_sampler` 里** —— 换成 `rng.normal(0, 1, size)` 就是高斯游走,
|
||||
换成带正漂移的增量就是有趋势的游走。
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> r = random_walk(n_steps=100, n_paths=5, random_state=0)
|
||||
>>> r.data.shape
|
||||
(5, 101)
|
||||
>>> bool(np.all(np.abs(np.diff(r.data, axis=1)) == 1)) # 默认步长恒为 1
|
||||
True
|
||||
"""
|
||||
# TODO(L1)
|
||||
raise NotImplementedError
|
||||
if isinstance(n_steps, bool) or not isinstance(n_steps, (int, np.integer)) or n_steps <= 0:
|
||||
raise ValueError(f"n_steps 必须是正整数,你传入了 {n_steps}")
|
||||
if isinstance(n_paths, bool) or not isinstance(n_paths, (int, np.integer)) or n_paths <= 0:
|
||||
raise ValueError(f"n_paths 必须是正整数,你传入了 {n_paths}")
|
||||
if not isinstance(start, (int, float, np.number)):
|
||||
raise TypeError(f"start 必须是数值,你传入了 {type(start).__name__}")
|
||||
|
||||
rng = get_rng(random_state)
|
||||
|
||||
if step_sampler is None:
|
||||
def _default_step_sampler(rng, size):
|
||||
return rng.integers(0, 2, size=size) * 2 - 1
|
||||
|
||||
step_sampler = _default_step_sampler
|
||||
|
||||
try:
|
||||
steps = np.asarray(step_sampler(rng, size=(n_paths, n_steps)), dtype=float)
|
||||
|
||||
cumsum = np.cumsum(steps, axis=1)
|
||||
|
||||
paths = np.zeros((n_paths, n_steps + 1), dtype=float)
|
||||
paths[:, 0] = start
|
||||
paths[:, 1:] = start + cumsum
|
||||
|
||||
return Result(
|
||||
name="random_walk",
|
||||
params={"n_steps": n_steps, "n_paths": n_paths, "start": start},
|
||||
metrics={"n_steps": n_steps, "n_paths": n_paths},
|
||||
data=paths,
|
||||
success=True,
|
||||
message=f"随机游走模拟成功:生成了 {n_paths} 条路径,每条路径 {n_steps} 步。"
|
||||
)
|
||||
except Exception as e:
|
||||
return Result(
|
||||
name="random_walk",
|
||||
params={"n_steps": n_steps, "n_paths": n_paths, "start": start},
|
||||
metrics={},
|
||||
data=None,
|
||||
success=False,
|
||||
message=f"随机游走模拟失败:{type(e).__name__} - {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
def markov_simulate(
|
||||
|
||||
Reference in new issue
Block a user