thrust_rl/buffer/rollout/
sampling.rs1use super::storage::RolloutBuffer;
7
8pub fn generate_minibatch_indices(buffer_size: usize, batch_size: usize) -> Vec<Vec<usize>> {
21 use rand::seq::SliceRandom;
22
23 let mut indices: Vec<usize> = (0..buffer_size).collect();
24 indices.shuffle(&mut rand::rng());
25
26 indices.chunks(batch_size).map(|chunk| chunk.to_vec()).collect()
27}
28
29pub fn sample_minibatch(buffer: &RolloutBuffer, indices: &[usize]) -> Minibatch {
38 let batch_size = indices.len();
39
40 let mut observations = vec![0.0; batch_size * buffer.shape().2];
41 let mut actions = vec![0; batch_size];
42 let mut old_log_probs = vec![0.0; batch_size];
43 let mut old_values = vec![0.0; batch_size];
44 let mut advantages = vec![0.0; batch_size];
45 let mut returns = vec![0.0; batch_size];
46
47 let flat_obs = buffer.observations().iter().flatten().flatten().cloned().collect::<Vec<f32>>();
49
50 let flat_actions = buffer.actions().iter().flatten().cloned().collect::<Vec<i64>>();
51
52 let flat_log_probs = buffer.log_probs().iter().flatten().cloned().collect::<Vec<f32>>();
53
54 let flat_values = buffer.values().iter().flatten().cloned().collect::<Vec<f32>>();
55
56 let flat_advantages = buffer.advantages().iter().flatten().cloned().collect::<Vec<f32>>();
57
58 let flat_returns = buffer.returns().iter().flatten().cloned().collect::<Vec<f32>>();
59
60 for (i, &idx) in indices.iter().enumerate() {
62 let obs_start = idx * buffer.shape().2;
64 let obs_end = obs_start + buffer.shape().2;
65 observations[i * buffer.shape().2..(i + 1) * buffer.shape().2]
66 .copy_from_slice(&flat_obs[obs_start..obs_end]);
67
68 actions[i] = flat_actions[idx];
69 old_log_probs[i] = flat_log_probs[idx];
70 old_values[i] = flat_values[idx];
71 advantages[i] = flat_advantages[idx];
72 returns[i] = flat_returns[idx];
73 }
74
75 Minibatch {
76 observations,
77 actions,
78 old_log_probs,
79 old_values,
80 advantages,
81 returns,
82 obs_dim: buffer.shape().2,
83 }
84}
85
86#[derive(Debug, Clone)]
90pub struct Minibatch {
91 pub observations: Vec<f32>,
93
94 pub actions: Vec<i64>,
96
97 pub old_log_probs: Vec<f32>,
99
100 pub old_values: Vec<f32>,
102
103 pub advantages: Vec<f32>,
105
106 pub returns: Vec<f32>,
108
109 obs_dim: usize,
111}
112
113impl Minibatch {
114 pub fn size(&self) -> usize {
116 self.actions.len()
117 }
118
119 pub fn obs_shape(&self) -> (usize, usize) {
121 (self.size(), self.obs_dim)
122 }
123
124 pub fn is_empty(&self) -> bool {
126 self.size() == 0
127 }
128}
129
130pub struct MinibatchIterator<'a> {
135 buffer: &'a RolloutBuffer,
136 indices: Vec<Vec<usize>>,
137 current_batch: usize,
138}
139
140impl<'a> MinibatchIterator<'a> {
141 pub fn new(buffer: &'a RolloutBuffer, batch_size: usize, shuffle: bool) -> Self {
148 let buffer_size = buffer.len();
149 let indices = if shuffle {
150 generate_minibatch_indices(buffer_size, batch_size)
151 } else {
152 (0..buffer_size)
153 .collect::<Vec<_>>()
154 .chunks(batch_size)
155 .map(|chunk| chunk.to_vec())
156 .collect()
157 };
158
159 Self { buffer, indices, current_batch: 0 }
160 }
161}
162
163impl<'a> Iterator for MinibatchIterator<'a> {
164 type Item = Minibatch;
165
166 fn next(&mut self) -> Option<Self::Item> {
167 if self.current_batch >= self.indices.len() {
168 return None;
169 }
170
171 let batch_indices = &self.indices[self.current_batch];
172 self.current_batch += 1;
173
174 Some(sample_minibatch(self.buffer, batch_indices))
175 }
176}
177
178pub fn shuffle_indices(size: usize) -> Vec<usize> {
189 use rand::seq::SliceRandom;
190
191 let mut indices: Vec<usize> = (0..size).collect();
192 indices.shuffle(&mut rand::rng());
193 indices
194}
195
196pub fn train_val_split(buffer: &RolloutBuffer, train_ratio: f32) -> (Vec<usize>, Vec<usize>) {
207 let total_size = buffer.len();
208 let train_size = ((total_size as f32) * train_ratio) as usize;
209
210 let indices: Vec<usize> = (0..total_size).collect();
211 let train_indices = indices[..train_size].to_vec();
215 let val_indices = indices[train_size..].to_vec();
216
217 (train_indices, val_indices)
218}