You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[BugFix] Fix GAE compact path bias on recurrent value nets at internal truncations
`_call_value_net_compact` previously built `data_in = [root[0:T], boundary]`
along the time dim and read `value_[t] = V(data_in[t+1])`. For every internal
`done[t]=True` (`t < T-1`), that read was `V(root_obs[t+1])` -- the
post-reset first observation of the next episode -- rather than the
env-returned `("next", obs)[t]` (the true pre-reset truncation observation).
GAE bootstraps with `(1 - terminated)`, so on envs that truncate without
terminating (Isaac-Ant, Pendulum-on-timeout, ...) the wrong
`next_state_value` was not masked out and flowed straight into the value
target. With recurrent value nets the wrong observation also corrupted the
LSTM hidden state going forward, cascading into downstream slots. The wandb
runs `g71pk34w` / `x05igvw7` (`shifted='compact'`) trailed `sln6yf2a` /
`6c8ihgh7` (`shifted=False`) by ~20% end-of-traj reward at iter 1000 for
exactly this reason.
The fix replaces the `T+1` interleave with a fused batched call: the root
and `("next", ...)` streams are concatenated along a non-time batch dim
into a constant-shape `[2*B, T, *F]` tensor and the value net is invoked
once. Reads of `value` and `value_` are simple batch-half slices. The
`("final", k)` collector contract still overrides the next side at slot
`T-1`. For recurrent value nets `("next", "is_init") |= root_is_init` is
applied so the LSTM resets at every trajectory boundary, exactly matching
the `shifted=False` reference (verified byte-exact on the regression
fixture). `_fill_missing_next_inputs` handles `compact_obs=True` rollouts
that don't populate `("next", k)`. A `time_idx == 0` guard keeps 1D
rollouts correct by unsqueezing a batch dim before the cat.
Shape stays constant across calls in a training run (no `.item()` syncs,
no Python branching on tensor values), so `torch.compile` and `vmap`
remain happy.
The regression test `test_gae_recurrent_shifted_compact_matches_unshifted_isaac_shape`
asserts compact matches `shifted=False` to within `rel < 0.05` on an
Isaac-shaped multi-trajectory rollout (`B=4`, `T=16`, truncations every 4
steps, `compact_obs=False` semantics). Pre-fix LSTM/GRU rel-err: 0.52 /
0.52. Post-fix: < 1e-7 (FP noise) for both. All other 1972 tests in
`test/objectives/test_values.py` continue to pass.
ghstack-source-id: f2d1a3e
Pull-Request: #3771
0 commit comments