Write a schedule¶
A schedule turns a run non-stationary: named regimes, durations, axes. The full key reference lists every field; this is the working pattern.
from skyfall_crl.regime import RegimeSchedule, ScheduledRegime
provider = ScheduledRegime(RegimeSchedule([
{"regime_id": "baseline", "duration_steps": 300, "ticket_arrival_rate": 0.2},
{"regime_id": "surge", "duration_steps": 300, "ticket_arrival_rate": 0.8},
{"regime_id": "baseline_revisit", "alias_of": "baseline", "duration_steps": 300},
]))
for step in (0, 299, 300, 650):
print(f"step {step:>4}: {provider.regime_id(step)}")
print(f"revisit repeats: {provider.regime_at(700).origin}")
step 0: baseline
step 299: baseline
step 300: surge
step 650: baseline_revisit
revisit repeats: baseline
The same mapping goes into an experiment document under regime.params.schedule, or into its own
YAML file.
The three rules that matter:
- Bring something back. The
alias_ofentry is what makes forgetting measurable — the same axes under a new label, with the origin recorded so both labels report one number. A schedule that never returns to anything cannot measure forgetting, and the table will show--. - Pick how shifts land.
shift_mode: abrupt(default) switches whole at the boundary;linear_drifteases numeric axes overdrift_windowsteps — a different experimental question;cyclicwraps the entries for as long as the run lasts. - Axes are open, and typos are guarded. Any key that is not
regime_id,duration_stepsoralias_ofis an axis. A near-miss of a known axis warns (tickt_arival_rateis never intentional); a key literally namedaxesis rejected as nesting-by-mistake; a genuinely new name passes — and is only meaningful if something (your wrapper, a task view) reads it.