canado / scripts /example_generator /generator.py
funny790's picture
Upload folder using huggingface_hub (part 4)
bb02ad0 verified
Raw
History Blame Contribute Delete
2.6 kB
"""Reference cascade generator — a template miners can fork.
Emits deterministic synthetic univariate series: a mix of trend, multi-seasonal
sinusoids, AR(1) noise, and occasional level shifts. Everything is driven by the
single ``seed`` passed in, so two runs at the same seed produce byte-identical
corpora — the determinism the trainer audits.
The submitted class MUST be named ``Generator`` and subclass
``cascade.interface.DataGenerator``. In a real submission you only need the
``cascade`` package importable to subclass it; the heavy lifting is your data
process.
"""
from __future__ import annotations
import json
from collections.abc import Iterator
from pathlib import Path
import numpy as np
from cascade.interface import DataGenerator
class Generator(DataGenerator):
def __init__(self, config_dir: str, *, seed: int) -> None:
cfg_path = Path(config_dir) / "config.json"
self._cfg = json.loads(cfg_path.read_text(encoding="utf-8")) if cfg_path.is_file() else {}
self._seed = int(seed)
self._min_len = int(self._cfg.get("min_length", 128))
self._max_len = int(self._cfg.get("max_length", 1024))
@property
def name(self) -> str:
return "reference-trend-seasonal-ar1"
def generate(self, n_series: int) -> Iterator[np.ndarray]:
rng = np.random.default_rng(self._seed)
for _ in range(n_series):
length = int(rng.integers(self._min_len, self._max_len + 1))
t = np.arange(length, dtype=np.float64)
# Trend.
slope = rng.normal(0.0, 0.01)
level = rng.normal(0.0, 1.0)
series = level + slope * t
# One or two seasonal components.
for _ in range(int(rng.integers(1, 3))):
period = float(rng.choice([7, 12, 24, 30, 52]))
amp = rng.uniform(0.2, 2.0)
phase = rng.uniform(0.0, 2.0 * np.pi)
series += amp * np.sin(2.0 * np.pi * t / period + phase)
# AR(1) noise.
phi = rng.uniform(0.0, 0.8)
sigma = rng.uniform(0.1, 0.5)
noise = np.empty(length, dtype=np.float64)
noise[0] = rng.normal(0.0, sigma)
for i in range(1, length):
noise[i] = phi * noise[i - 1] + rng.normal(0.0, sigma)
series += noise
# Occasional level shift.
if rng.random() < 0.2:
shift_at = int(rng.integers(length // 4, 3 * length // 4))
series[shift_at:] += rng.normal(0.0, 2.0)
yield series.astype(np.float64)