Download src/utils/random_utils.py from InternsProgressUpdate/Venket: direct link, hf CLI and curl.
- Browser
- Download file 3.72 kB
-
https://huggingface.co/InternsProgressUpdate/Venket/resolve/main/src/utils/random_utils.py
- Command line
-
hf download hf://InternsProgressUpdate/Venket/src/utils/random_utils.py
-
curl -L -o random_utils.py https://huggingface.co/InternsProgressUpdate/Venket/resolve/main/src/utils/random_utils.py
3.72 kB
| """Deterministic random-state management. | |
| The generator must produce byte-identical output for a given ``(master_seed, | |
| sample_index)`` pair regardless of process/thread scheduling, batch size, or | |
| worker count. To guarantee this we never share a single global RNG across | |
| samples or pipeline stages. Instead, every stage of every sample draws from | |
| an independently *derived* RNG whose seed is a pure function of | |
| ``(master_seed, sample_index, stage_name)``. | |
| This makes the pipeline safe for multiprocessing: worker N generating sample | |
| 57 produces exactly the same random draws as a single-process run would have, | |
| because nothing depends on execution order. | |
| """ | |
| from __future__ import annotations | |
| import hashlib | |
| import random | |
| from typing import Any | |
| import numpy as np | |
| _MOD32 = 2**32 | |
| def derive_seed(master_seed: int, *parts: Any) -> int: | |
| """Derive a deterministic 32-bit seed from a master seed and arbitrary parts. | |
| Uses SHA-256 over the concatenated string representation of all parts so | |
| that the mapping is stable across Python versions and processes (unlike | |
| Python's salted ``hash()``). | |
| Args: | |
| master_seed: The top-level seed configured by the user. | |
| *parts: Any number of hashable/stringifiable disambiguators, e.g. | |
| sample index and pipeline stage name. | |
| Returns: | |
| A non-negative integer in ``[0, 2**32)`` suitable for seeding | |
| ``numpy.random.Generator`` or ``random.Random``. | |
| """ | |
| key = f"{master_seed}|" + "|".join(str(p) for p in parts) | |
| digest = hashlib.sha256(key.encode("utf-8")).digest() | |
| return int.from_bytes(digest[:4], byteorder="big") % _MOD32 | |
| class SeedManager: | |
| """Provides independent, deterministic RNGs for each pipeline stage of one sample. | |
| Example: | |
| >>> sm = SeedManager(master_seed=42, sample_index=7) | |
| >>> rng = sm.numpy_rng("placement") | |
| >>> rng.uniform(0, 1) | |
| """ | |
| def __init__(self, master_seed: int, sample_index: int) -> None: | |
| self.master_seed = master_seed | |
| self.sample_index = sample_index | |
| def seed_for(self, stage: str) -> int: | |
| """Return the deterministic integer seed for a named pipeline stage.""" | |
| return derive_seed(self.master_seed, self.sample_index, stage) | |
| def numpy_rng(self, stage: str) -> np.random.Generator: | |
| """Return a fresh ``numpy.random.Generator`` (PCG64) seeded for ``stage``.""" | |
| return np.random.default_rng(self.seed_for(stage)) | |
| def python_rng(self, stage: str) -> random.Random: | |
| """Return a fresh ``random.Random`` seeded for ``stage``.""" | |
| return random.Random(self.seed_for(stage)) | |
| def resolve_scalar_or_range(value: Any, rng: np.random.Generator) -> float: | |
| """Resolve a config parameter that is either a fixed scalar or a ``{min, max}`` range. | |
| Args: | |
| value: Either a plain number, or an object/mapping exposing ``min`` | |
| and ``max`` attributes/keys (a :class:`~src.config.schema.Range` | |
| or an equivalent dict). | |
| rng: The seeded generator to draw from when ``value`` is a range. | |
| Returns: | |
| A resolved float value. | |
| """ | |
| if isinstance(value, (int, float)): | |
| return float(value) | |
| if isinstance(value, dict) and "min" in value and "max" in value: | |
| lo, hi = float(value["min"]), float(value["max"]) | |
| return float(rng.uniform(lo, hi)) if hi > lo else lo | |
| # Pydantic Range model (duck-typed access) | |
| lo = getattr(value, "min", None) | |
| hi = getattr(value, "max", None) | |
| if lo is not None and hi is not None: | |
| lo, hi = float(lo), float(hi) | |
| return float(rng.uniform(lo, hi)) if hi > lo else lo | |
| raise TypeError(f"Cannot resolve scalar-or-range value: {value!r}") | |