Skip to content

Commit f2d59ce

Browse files
committed
center padding mode, on_interact_step hook
1 parent cb55f00 commit f2d59ce

2 files changed

Lines changed: 24 additions & 1 deletion

File tree

amago/experiment.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class Experiment:
7777
:param wandb_group_name: Group runs on wandb dashboard. **Default:** None.
7878
:param verbose: Print tqdm bars and info to console. **Default:** True.
7979
:param log_interval: Log extra metrics every N batches. **Default:** 300.
80-
:param padded_sampling: Padding for sampling training subsequences. "none", "left", "right", "both". **Default:** "none".
80+
:param padded_sampling: Padding for sampling training subsequences. "none", "left", "right", "both", "center". **Default:** "none".
8181
:param dloader_workers: Number of DataLoader workers for disk loading. Increase for compressed/large trajs.
8282
8383
.. note::
@@ -522,6 +522,15 @@ def policy(self) -> BaseAgent:
522522
"""Returns the current Agent policy free from the accelerator wrapper."""
523523
return self.accelerator.unwrap_model(self.policy_aclr)
524524

525+
def on_interact_step(self, obs: dict) -> None:
526+
"""Hook called before each action during ``self.interact(...)``.
527+
528+
Override in a subclass to mutate agent state between steps
529+
(e.g. resample ``agent._inference_alpha`` at episode boundaries by
530+
inspecting ``obs["episode_id"]``). No-op in the base implementation.
531+
"""
532+
pass
533+
525534
def interact(
526535
self,
527536
envs,
@@ -603,6 +612,7 @@ def get_t():
603612
obs, rl2s, time_idxs = get_t()
604613
episodes_finished = 0
605614
for _ in iter_:
615+
self.on_interact_step(obs)
606616
with torch.no_grad():
607617
with self.caster():
608618
actions, hidden_state = policy.get_actions(

amago/loading.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ def random_slice(self, length: int, padded_sampling: str = "none"):
9090
where the start of the training sequence is not always the first timestep of the trajectory.
9191
"right" --> sample while effectively padding the right side of the sequence for cases
9292
where the end of the trajectory may be undersampled.
93+
"center" --> uniform random window center, clamped to valid range.
94+
Always returns a full-length (never padded) window. Fixes endpoint
95+
undersampling of "none" when the trajectory is longer than `length`.
9396
"""
9497
if len(self) <= length:
9598
start = 0
@@ -101,6 +104,16 @@ def random_slice(self, length: int, padded_sampling: str = "none"):
101104
start = self._safe_randrange(-length + 1, len(self) - length + 1)
102105
elif padded_sampling == "right":
103106
start = self._safe_randrange(0, len(self) - 1)
107+
elif padded_sampling == "center":
108+
# Uniform center over [0, T), window clamped to fit inside [0, T].
109+
# Always length `length`, never padded. Compared to "none", which
110+
# severely undersamples the first/last (length - 1) timesteps,
111+
# this mode gives them ~length/T marginal coverage instead of
112+
# ~1/(T-length+1). Trade-off: interior timesteps still get more
113+
# coverage than endpoints when T < 2*length - 1, but the
114+
# imbalance is dramatically smaller than "none".
115+
center = self._safe_randrange(0, len(self))
116+
start = min(max(center - length // 2, 0), len(self) - length)
104117
else:
105118
raise ValueError(
106119
f"Unrecognized `padded_sampling` mode: `{padded_sampling}`"

0 commit comments

Comments
 (0)