factoriax.analysis.recorder#
Collect rollout chunks from a training loop into a trajectory.
RolloutRecorder takes the arrays that a collection function
already returns and turns them into a
Trajectory. A training loop
therefore needs one new call, and no change to collect_fn or to
any code under jit.
Call record() after each collection call, then
finish() once at the end.
The sketch below names collect_fn and total_iters from the
training loop it plugs into, so it does not run on its own:
from factoriax.analysis.recorder import RolloutRecorder
recorder = RolloutRecorder(max_episodes=32)
for _ in range(total_iters):
trajectories, env_states, obs, last_values, _ = collect_fn(...)
recorder.record(trajectories, env_states)
traj = recorder.finish()
- class factoriax.analysis.recorder.RolloutRecorder(max_episodes: int | None = None, record_states: bool = False, state_fields: list[str] = <factory>, _action_chunks: list[ndarray] = <factory>, _reward_chunks: list[ndarray] = <factory>, _done_chunks: list[ndarray] = <factory>, _state_chunks: dict[str, list[~numpy.ndarray]]=<factory>, _num_complete_episodes: int = 0)#
Accumulates rollout data across training iterations.
The recorder reads the arrays that a collection function already returns. A training loop therefore needs one new call, and no change to
collect_fnor to any code underjit.Each call to
record()stores one(T, N)chunk.finish()joins the chunks and cuts them into episodes at thedoneflags.- max_episodes#
Stop storing chunks once this many episodes have finished, and keep at most this many episodes in the result.
Nonemeans no limit, andfinish()then decides the count.- Type:
int | None
- record_states#
True to also store the fields named in
state_fieldsfrom the env states. This costs memory in proportion to the size of those fields, and it is what makes a state-over-time analysis possible.- Type:
Notes
The episode count in
is_fullcountsdoneflags across every parallel environment, and not per environment.- finish(pad_incomplete: bool = True) Trajectory#
Cut the recorded chunks into episodes and build a trajectory.
The chunks are joined along the time axis and then split at the
doneflags, one environment at a time. State fields are cut at the same boundaries.Episodes differ in length, so every episode is padded with zeros to the length of the longest one.
- Parameters:
pad_incomplete – True to keep the last episode of each environment even when no
doneflag ended it. False to keep finished episodes only.- Returns:
Actions of shape
(B, T_max)or(B, T_max, P), plus rewards, plus any recorded state fields under their trajectory names.Bis the episode count after themax_episodeslimit.- Return type:
- Raises:
ValueError – When no chunk was recorded, or when the split found no episode at all. The second case happens with
pad_incomplete=Falseand nodoneflag anywhere.
Notes
The padding is zeros, and action 0 is
NOOP. Padded steps therefore read as real no-op actions to every analysis function, and the trajectory carries no mask that marks them. A per-episode length is the only way to tell them apart, and this method does not return one.The method does not clear the buffers. A second call rebuilds the same trajectory. Call
reset()to start again.
- property is_full: bool#
Whether the episode count has reached
max_episodes.- Returns:
Always False when
max_episodesisNone.- Return type:
- record(trajectories: Any, env_states: Any = None) None#
Store one chunk of rollout data.
The call returns at once when the recorder is already full, so a training loop can keep calling it without a guard. The chunk is then dropped.
- Parameters:
trajectories – The struct that
collect_fnreturns. It must carryaction,reward, anddone. Actions have shape(T, N)or(T, N, P), and the other two(T, N), whereTis the chunk length andNthe number of parallel environments. JAX arrays and NumPy arrays both work.env_states – The env states from the same call, or
None. They are read only whenrecord_statesis True. A field named instate_fieldsbut absent from the states is skipped in silence.