Skip to main content

thrust_rl/buffer/
rollout.rs

1//! Rollout buffer for storing and processing trajectories
2//!
3//! This module implements experience storage for PPO training, including:
4//! - Trajectory storage (observations, actions, rewards, etc.)
5//! - Generalized Advantage Estimation (GAE) computation
6//! - Efficient sampling for minibatch training
7//!
8//! # Buffer Layout
9//!
10//! The buffer uses a `[num_steps, num_envs]` layout where:
11//! - `num_steps`: Number of timesteps per rollout (typically 128-2048)
12//! - `num_envs`: Number of parallel environments
13//!
14//! This layout provides good cache locality for forward passes and
15//! efficient computation of advantages.
16
17// Re-export main components
18pub use gae::{
19    compute_advantages, compute_advantages_multi_agent, compute_advantages_partial,
20    compute_mc_returns, compute_nstep_returns, normalize_advantages,
21};
22pub use sampling::{
23    Minibatch, MinibatchIterator, generate_minibatch_indices, sample_minibatch, shuffle_indices,
24    train_val_split,
25};
26pub use storage::{RolloutBatch, RolloutBuffer, RolloutBurnTensors};
27
28// Submodules
29mod gae;
30mod sampling;
31mod storage;
32
33#[cfg(test)]
34mod tests;
35
36// Legacy interface - re-export compute_advantages as a method on RolloutBuffer
37impl RolloutBuffer {
38    /// Compute advantages using Generalized Advantage Estimation
39    ///
40    /// This is a convenience method that calls the module-level function.
41    /// Iterates the full `[num_steps, num_envs]` capacity; use
42    /// [`Self::compute_advantages_partial`] when the buffer is only
43    /// partially filled.
44    ///
45    /// # Arguments
46    /// * `last_values` - Value estimates for the final states `[num_envs]`
47    /// * `gamma` - Discount factor
48    /// * `gae_lambda` - GAE lambda parameter
49    pub fn compute_advantages(&mut self, last_values: &[f32], gamma: f32, gae_lambda: f32) {
50        gae::compute_advantages(self, last_values, gamma, gae_lambda);
51    }
52
53    /// Compute advantages over the first `valid_steps` rows only.
54    ///
55    /// Convenience wrapper around the module-level
56    /// [`compute_advantages_partial`]. Use this when the rollout buffer
57    /// has been partially filled (`valid_steps < num_steps`) to prevent
58    /// zero-padded tail rows from contaminating GAE on the real prefix.
59    ///
60    /// # Arguments
61    /// * `valid_steps` - Number of filled rows at the start of the buffer
62    /// * `last_values` - Bootstrap `V(s_{T+1})` for the state after row
63    ///   `valid_steps - 1`, per environment `[num_envs]`
64    /// * `gamma` - Discount factor
65    /// * `gae_lambda` - GAE lambda parameter
66    pub fn compute_advantages_partial(
67        &mut self,
68        valid_steps: usize,
69        last_values: &[f32],
70        gamma: f32,
71        gae_lambda: f32,
72    ) {
73        gae::compute_advantages_partial(self, valid_steps, last_values, gamma, gae_lambda);
74    }
75
76    /// Get a batch of all data from the buffer
77    ///
78    /// This is a convenience method that calls the module-level function.
79    /// Returns the full `[num_steps, num_envs]` capacity, including any
80    /// zero-padded unwritten tail; use [`Self::get_filled_batch`] when
81    /// the buffer is only partially filled.
82    pub fn get_batch(&self) -> RolloutBatch {
83        RolloutBatch::from_buffer(self)
84    }
85
86    /// Get a batch covering only the first `valid_steps` rows.
87    ///
88    /// Use this when the buffer was filled with fewer than `num_steps`
89    /// transitions (e.g. an early-terminating rollout). It prevents the
90    /// zero-initialized tail of the buffer from being handed to PPO as
91    /// fake training data.
92    ///
93    /// # Panics
94    /// Panics if `valid_steps > self.shape().0`.
95    pub fn get_filled_batch(&self, valid_steps: usize) -> RolloutBatch {
96        RolloutBatch::from_buffer_partial(self, valid_steps)
97    }
98}