burn_train/learner/rl/env_runner/
base.rs1use burn_core::Tensor;
2use burn_core::data::dataloader::Progress;
3use burn_core::tensor::Device;
4use burn_rl::Policy;
5use burn_rl::ToObservation;
6use burn_rl::Transition;
7use burn_rl::{Environment, EnvironmentInit};
8
9use crate::RLEvent;
10use crate::{
11 AgentEvaluationEvent, EpisodeSummary, EvaluationItem, EventProcessorTraining,
12 RLEventProcessorType,
13};
14use crate::{Interrupter, RLComponentsTypes};
15
16#[derive(Clone, new)]
18pub struct Trajectory<S, A, C> {
19 pub timesteps: Vec<TimeStep<S, A, C>>,
21}
22
23#[derive(Clone)]
25pub struct TimeStep<S, A, C> {
26 pub env_id: usize,
28 pub transition: Transition<S, A>,
30 pub done: bool,
32 pub ep_len: usize,
34 pub cum_reward: f64,
36 pub action_context: C,
38}
39
40pub(crate) type RLTimeStep<RLC> = TimeStep<
41 <RLC as RLComponentsTypes>::State,
42 <RLC as RLComponentsTypes>::Action,
43 <RLC as RLComponentsTypes>::ActionContext,
44>;
45
46pub(crate) type RLTrajectory<RLC> = Trajectory<
47 <RLC as RLComponentsTypes>::State,
48 <RLC as RLComponentsTypes>::Action,
49 <RLC as RLComponentsTypes>::ActionContext,
50>;
51
52pub trait AgentEnvLoop<RLC: RLComponentsTypes> {
54 fn run_steps(
68 &mut self,
69 num_steps: usize,
70 processor: &mut RLEventProcessorType<RLC>,
71 interrupter: &Interrupter,
72 progress: &mut Progress,
73 ) -> Vec<RLTimeStep<RLC>>;
74 fn run_episodes(
87 &mut self,
88 num_episodes: usize,
89 processor: &mut RLEventProcessorType<RLC>,
90 interrupter: &Interrupter,
91 progress: &mut Progress,
92 ) -> Vec<RLTrajectory<RLC>>;
93 fn update_policy(&mut self, update: RLC::PolicyState);
95 fn policy(&self) -> RLC::PolicyState;
97 fn device(&self) -> Device;
99}
100
101pub struct AgentEnvBaseLoop<RLC: RLComponentsTypes> {
103 env: RLC::Env,
104 eval: bool,
105 agent: RLC::Policy,
106 deterministic: bool,
107 current_reward: f64,
108 run_num: usize,
109 step_num: usize,
110 device: Device,
111}
112
113impl<RLC: RLComponentsTypes> AgentEnvBaseLoop<RLC> {
114 pub fn new(
116 env_init: RLC::EnvInit,
117 agent: RLC::Policy,
118 eval: bool,
119 deterministic: bool,
120 device: &Device,
121 ) -> Self {
122 let agent = agent.to_device(device);
123 let mut env = env_init.init();
124 env.reset();
125
126 Self {
127 env,
128 eval,
129 agent: agent.clone(),
130 deterministic,
131 current_reward: 0.0,
132 run_num: 0,
133 step_num: 0,
134 device: device.clone(),
135 }
136 }
137}
138
139impl<RLC> AgentEnvLoop<RLC> for AgentEnvBaseLoop<RLC>
140where
141 RLC: RLComponentsTypes,
142{
143 fn run_steps(
144 &mut self,
145 num_steps: usize,
146 processor: &mut RLEventProcessorType<RLC>,
147 interrupter: &Interrupter,
148 progress: &mut Progress,
149 ) -> Vec<RLTimeStep<RLC>> {
150 let mut items = vec![];
151 let device = Default::default();
152 for _ in 0..num_steps {
153 let state = self.env.state();
154 let (action, context) = self.agent.action(
155 state.clone().to_observation(&self.device),
156 self.deterministic,
157 );
158
159 let step_result = self.env.step(RLC::Action::from(action.clone()));
160
161 self.current_reward += step_result.reward;
162 self.step_num += 1;
163
164 let transition = Transition::new(
165 state.clone(),
166 step_result.next_state,
167 RLC::Action::from(action),
168 Tensor::from_data([step_result.reward], &device),
169 Tensor::from_data(
170 [(step_result.done || step_result.truncated) as i32 as f64],
171 &device,
172 ),
173 );
174 items.push(TimeStep {
175 env_id: 0,
176 transition,
177 done: step_result.done,
178 ep_len: self.step_num,
179 cum_reward: self.current_reward,
180 action_context: context[0].clone(),
181 });
182
183 if !self.eval {
184 progress.items_processed += 1;
185 interrupter.fail_on_error(processor.process_train(RLEvent::EnvStep(
186 EvaluationItem::new(context[0].clone(), progress.clone(), None),
187 )));
188
189 if step_result.done {
190 interrupter.fail_on_error(processor.process_train(RLEvent::EpisodeEnd(
191 EvaluationItem::new(
192 EpisodeSummary {
193 episode_length: self.step_num,
194 cum_reward: self.current_reward,
195 },
196 progress.clone(),
197 None,
198 ),
199 )));
200 }
201 }
202
203 if interrupter.should_stop() {
204 break;
205 }
206
207 if step_result.done || step_result.truncated {
208 self.env.reset();
209 self.current_reward = 0.;
210 self.step_num = 0;
211 self.run_num += 1;
212 }
213 }
214 items
215 }
216
217 fn update_policy(&mut self, update: RLC::PolicyState) {
218 self.agent.update(update);
219 }
220
221 fn run_episodes(
222 &mut self,
223 num_episodes: usize,
224 processor: &mut RLEventProcessorType<RLC>,
225 interrupter: &Interrupter,
226 progress: &mut Progress,
227 ) -> Vec<RLTrajectory<RLC>> {
228 self.env.reset();
229
230 let mut items = vec![];
231 for ep in 0..num_episodes {
232 let mut steps = vec![];
233 loop {
234 let step = self.run_steps(1, processor, interrupter, progress)[0].clone();
235 steps.push(step.clone());
236
237 if self.eval {
238 interrupter.fail_on_error(processor.process_valid(
239 AgentEvaluationEvent::EnvStep(EvaluationItem::new(
240 step.action_context.clone(),
241 Progress::new(
242 steps.len() + 1,
243 steps.len() + 1,
244 Some("steps".to_string()),
245 ),
246 None,
247 )),
248 ));
249
250 if step.done {
251 interrupter.fail_on_error(processor.process_valid(
252 AgentEvaluationEvent::EpisodeEnd(EvaluationItem::new(
253 EpisodeSummary {
254 episode_length: step.ep_len,
255 cum_reward: step.cum_reward,
256 },
257 Progress::new(ep + 1, num_episodes, Some("episodes".to_string())),
258 None,
259 )),
260 ));
261 }
262 }
263
264 if interrupter.should_stop() || step.done {
265 break;
266 }
267 }
268 items.push(Trajectory::new(steps));
269
270 if interrupter.should_stop() {
271 break;
272 }
273 }
274 items
275 }
276
277 fn policy(&self) -> RLC::PolicyState {
278 self.agent.state()
279 }
280
281 fn device(&self) -> Device {
282 self.device.clone()
283 }
284}
285
286#[cfg(test)]
287#[allow(clippy::needless_range_loop)]
288mod tests {
289 use crate::AsyncProcessorTraining;
290
291 use crate::learner::tests::{
292 MockEnvInit, MockPolicy, MockPolicyState, MockProcessor, MockRLComponents,
293 };
294
295 use super::*;
296
297 fn setup(state: usize, eval: bool, deterministic: bool) -> AgentEnvBaseLoop<MockRLComponents> {
298 let env_init = MockEnvInit;
299 let agent = MockPolicy(state);
300 AgentEnvBaseLoop::<MockRLComponents>::new(
301 env_init,
302 agent,
303 eval,
304 deterministic,
305 &Default::default(),
306 )
307 }
308
309 #[test]
310 fn test_policy_returns_agent_state() {
311 let runner = setup(1000, false, false);
312 let policy_state = runner.policy();
313 assert_eq!(policy_state.0, 1000);
314 }
315
316 #[test]
317 fn test_update_policy() {
318 let mut runner = setup(0, false, false);
319
320 runner.update_policy(MockPolicyState(1));
321 assert_eq!(runner.policy().0, 1);
322 }
323
324 #[test]
325 fn run_steps_returns_requested_number() {
326 let mut runner = setup(0, false, false);
327 let mut processor = AsyncProcessorTraining::new(MockProcessor);
328 let interrupter = Interrupter::new();
329 let mut progress = Progress {
330 items_processed: 0,
331 items_total: 1,
332 unit: Some("steps".to_string()),
333 };
334
335 let steps = runner.run_steps(1, &mut processor, &interrupter, &mut progress);
336 assert_eq!(steps.len(), 1);
337 let steps = runner.run_steps(8, &mut processor, &interrupter, &mut progress);
338 assert_eq!(steps.len(), 8);
339 }
340
341 #[test]
342 fn run_episodes_returns_requested_number() {
343 let mut runner = setup(0, false, false);
344 let mut processor = AsyncProcessorTraining::new(MockProcessor);
345 let interrupter = Interrupter::new();
346 let mut progress = Progress {
347 items_processed: 0,
348 items_total: 1,
349 unit: Some("steps".to_string()),
350 };
351
352 let trajectories = runner.run_episodes(1, &mut processor, &interrupter, &mut progress);
353 assert_eq!(trajectories.len(), 1);
354 assert_ne!(trajectories[0].timesteps.len(), 0);
355 let trajectories = runner.run_episodes(8, &mut processor, &interrupter, &mut progress);
356 assert_eq!(trajectories.len(), 8);
357 for i in 0..8 {
358 assert_ne!(trajectories[i].timesteps.len(), 0);
359 }
360 }
361}