feat(core): 实现 get_rng 与 set_seed
L0 第 2 项落地。 - get_rng(int) 固定种子可复现;get_rng(Generator) 原样透传; get_rng() 跟随 set_seed 设过的全局种子,没设过则交给系统熵源 - set_seed(seed) 除记录 _GLOBAL_SEED 外,还调用 np.random.seed, 让 sklearn / statsmodels 这些走 numpy 旧全局状态的库也能复现; 这是全项目唯一碰旧接口的位置,标注了 noqa: NPY002 与理由 - 已知取舍写进 docstring:同一全局种子下多次 get_rng() 是同一条流 - tests/test_core.py:TestRandom 去掉 skip;把原来那条自证式的 test_set_seed_makes_global_reproducible 换成两条真断言 (不传 random_state 也要可复现、旧全局状态被覆盖) - STATUS.md:L0 进度 2/12,新增决策 23(允许 set_seed 碰旧接口) 与 24(不做 SeedSequence.spawn 拆分);CHANGELOG.md 同步
This commit is contained in:
1 parent
cb96c847dd
commit
658b266614
4 files changed
+62
-20
No files matched your search
@@ -4,6 +4,7 @@
|
||||
|
||||
* 所有含随机的公开函数签名都带 `random_state=None`,内部第一行取 rng。
|
||||
* 禁止使用 `np.random.*` 旧式全局接口,也禁止裸用标准库 `random`。
|
||||
(唯一例外是 :func:`set_seed` 内部的 `np.random.seed`,见该函数说明。)
|
||||
* 论文附录必须写明 seed —— 提交要能复现。
|
||||
"""
|
||||
|
||||
@@ -13,33 +14,51 @@ import numpy as np
|
||||
|
||||
__all__ = ["get_rng", "set_seed"]
|
||||
|
||||
# set_seed 记下来的全局种子;None 表示用户没设过。
|
||||
_GLOBAL_SEED: int | None = None
|
||||
|
||||
def get_rng(random_state: int | None = None) -> np.random.Generator:
|
||||
|
||||
def get_rng(random_state: int | np.random.Generator | None = None) -> np.random.Generator:
|
||||
"""取一个随机数生成器。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
random_state:
|
||||
`int` 时返回固定种子的独立生成器(可复现);
|
||||
`None` 时返回基于全局种子的生成器;
|
||||
`None` 时跟随 :func:`set_seed` 设过的全局种子,没设过就交给系统熵源;
|
||||
已经是 :class:`np.random.Generator` 时原样返回(便于函数间串联)。
|
||||
|
||||
Notes
|
||||
-----
|
||||
同一个全局种子下,每次 `get_rng()` 拿到的都是**同一条**流。这保证了
|
||||
"脚本重跑一遍结果一样",但也意味着两个都省略 `random_state` 的函数会共用
|
||||
同一串随机数。想避开,就显式传不同的 `random_state`。
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> rng = get_rng(42)
|
||||
>>> bool(rng.random() == get_rng(42).random())
|
||||
True
|
||||
"""
|
||||
# TODO(L0)
|
||||
raise NotImplementedError
|
||||
if isinstance(random_state, np.random.Generator):
|
||||
return random_state
|
||||
if random_state is None:
|
||||
random_state = _GLOBAL_SEED # 没设过就是 None,交给系统熵源
|
||||
return np.random.default_rng(random_state)
|
||||
|
||||
|
||||
def set_seed(seed: int) -> None:
|
||||
"""设置全局随机种子。
|
||||
|
||||
除了记住种子,还会一并喂给 numpy 的旧全局状态:sklearn / statsmodels
|
||||
内部仍在用它(`check_random_state(None)` 那条路),只设自己的
|
||||
`_GLOBAL_SEED` 的话这些库照样不可复现。
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> set_seed(42)
|
||||
"""
|
||||
# TODO(L0)
|
||||
raise NotImplementedError
|
||||
global _GLOBAL_SEED
|
||||
_GLOBAL_SEED = int(seed)
|
||||
# 这里是全项目唯一允许碰旧接口的地方:它是"设种子",不是"取随机数"。
|
||||
np.random.seed(_GLOBAL_SEED) # noqa: NPY002 - 故意的,见本函数 docstring
|
||||
Reference in new issue
Block a user