1use rand::prelude::SliceRandom;
2use std::{
3 sync::mpsc::{Receiver, Sender},
4 thread::spawn,
5};
6
7use burn_core::{Tensor, data::dataloader::Progress, tensor::Device};
8use burn_rl::Policy;
9use burn_rl::Transition;
10use burn_rl::{AsyncPolicy, Environment};
11use burn_rl::{EnvironmentInit, ToObservation};
12
13use crate::{
14 AgentEnvLoop, AgentEvaluationEvent, EpisodeSummary, EvaluationItem, EventProcessorTraining,
15 Interrupter, RLComponentsTypes, RLEvent, RLEventProcessorType, RLTimeStep, RLTrajectory,
16 RlPolicy, TimeStep, Trajectory,
17};
18
19enum RequestMessage {
20 Step(),
21 Episode(),
22}
23
24pub struct AsyncAgentEnvLoopConfig {
26 pub eval: bool,
28 pub deterministic: bool,
30 pub id: usize,
32}
33
34pub struct AgentEnvAsyncLoop<RLC: RLComponentsTypes> {
36 eval: bool,
37 agent: AsyncPolicy<RlPolicy<RLC>>,
38 transition_receiver: Receiver<RLTimeStep<RLC>>,
39 trajectory_receiver: Receiver<RLTrajectory<RLC>>,
40 request_sender: Sender<RequestMessage>,
41 device: Device,
42}
43
44impl<RLC: RLComponentsTypes> AgentEnvAsyncLoop<RLC> {
45 pub fn new(
59 env_init: RLC::EnvInit,
60 agent: AsyncPolicy<RlPolicy<RLC>>,
61 config: AsyncAgentEnvLoopConfig,
62 transition_device: &Device,
63 transition_sender: Option<Sender<RLTimeStep<RLC>>>,
64 trajectory_sender: Option<Sender<RLTrajectory<RLC>>>,
65 ) -> Self {
66 let (loop_transition_sender, transition_receiver) = std::sync::mpsc::channel();
67 let (loop_trajectory_sender, trajectory_receiver) = std::sync::mpsc::channel();
68 let (request_sender, request_receiver) = std::sync::mpsc::channel();
69 let loop_transition_sender = transition_sender.unwrap_or(loop_transition_sender);
70 let loop_trajectory_sender = trajectory_sender.unwrap_or(loop_trajectory_sender);
71
72 let device = transition_device.clone();
73 let mut loop_agent = agent.clone().to_device(transition_device);
74 let eval = config.eval;
75
76 let mut current_steps = vec![];
77 let mut current_reward = 0.0;
78 let mut step_num = 0;
79 spawn(move || {
80 let mut env = env_init.init();
81 env.reset();
82
83 let mut request_episode = false;
84 loop {
85 let state = env.state();
86 let (action, context) =
87 loop_agent.action(state.clone().to_observation(&device), config.deterministic);
88
89 let env_action = RLC::Action::from(action);
90 let step_result = env.step(env_action.clone());
91
92 current_reward += step_result.reward;
93 step_num += 1;
94
95 let transition = Transition::new(
96 state.clone(),
97 step_result.next_state,
98 env_action,
99 Tensor::from_data([step_result.reward], &device),
100 Tensor::from_data(
101 [(step_result.done || step_result.truncated) as i32 as f64],
102 &device,
103 ),
104 );
105
106 if !request_episode {
107 loop_agent.decrement_agents(1);
108 let request = match request_receiver.recv() {
109 Ok(req) => req,
110 Err(err) => {
111 log::error!("Error in env runner : {}", err);
112 break;
113 }
114 };
115 loop_agent.increment_agents(1);
116
117 match request {
118 RequestMessage::Step() => (),
119 RequestMessage::Episode() => request_episode = true,
120 }
121 }
122
123 let time_step = TimeStep {
124 env_id: config.id,
125 transition,
126 done: step_result.done,
127 ep_len: step_num,
128 cum_reward: current_reward,
129 action_context: context[0].clone(),
130 };
131 current_steps.push(time_step.clone());
132
133 if !request_episode && let Err(err) = loop_transition_sender.send(time_step) {
134 log::error!("Error in env runner : {}", err);
135 break;
136 }
137
138 if step_result.done || step_result.truncated {
139 if request_episode {
140 request_episode = false;
141 loop_trajectory_sender
142 .send(Trajectory {
143 timesteps: current_steps.clone(),
144 })
145 .expect("Can send trajectory to main thread.");
146 }
147 current_steps.clear();
148
149 env.reset();
150 current_reward = 0.;
151 step_num = 0;
152 }
153 }
154 });
155
156 Self {
157 eval,
158 agent,
159 transition_receiver,
160 trajectory_receiver,
161 request_sender,
162 device: transition_device.clone(),
163 }
164 }
165}
166
167impl<RLC> AgentEnvLoop<RLC> for AgentEnvAsyncLoop<RLC>
168where
169 RLC: RLComponentsTypes,
170{
171 fn run_steps(
172 &mut self,
173 num_steps: usize,
174 processor: &mut RLEventProcessorType<RLC>,
175 interrupter: &Interrupter,
176 progress: &mut Progress,
177 ) -> Vec<RLTimeStep<RLC>> {
178 let mut items = vec![];
179 for _ in 0..num_steps {
180 self.request_sender
181 .send(RequestMessage::Step())
182 .expect("Can request transitions.");
183 let transition = self
184 .transition_receiver
185 .recv()
186 .expect("Can receive transitions.");
187 items.push(transition.clone());
188
189 if !self.eval {
190 progress.items_processed += 1;
191 interrupter.fail_on_error(processor.process_train(RLEvent::EnvStep(
192 EvaluationItem::new(transition.action_context, progress.clone(), None),
193 )));
194
195 if transition.done {
196 interrupter.fail_on_error(processor.process_train(RLEvent::EpisodeEnd(
197 EvaluationItem::new(
198 EpisodeSummary {
199 episode_length: transition.ep_len,
200 cum_reward: transition.cum_reward,
201 },
202 progress.clone(),
203 None,
204 ),
205 )));
206 }
207 }
208
209 if interrupter.should_stop() {
210 break;
211 }
212 }
213 items
214 }
215
216 fn run_episodes(
217 &mut self,
218 num_episodes: usize,
219 processor: &mut RLEventProcessorType<RLC>,
220 interrupter: &Interrupter,
221 _progress: &mut Progress,
222 ) -> Vec<RLTrajectory<RLC>> {
223 let mut items = vec![];
224 self.agent.increment_agents(1);
225 for episode_num in 0..num_episodes {
226 self.request_sender
227 .send(RequestMessage::Episode())
228 .expect("Can request episodes.");
229 let trajectory = self
230 .trajectory_receiver
231 .recv()
232 .expect("Main thread can receive trajectory.");
233
234 for (i, step) in trajectory.timesteps.iter().enumerate() {
235 if self.eval {
237 interrupter.fail_on_error(processor.process_valid(
238 AgentEvaluationEvent::EnvStep(EvaluationItem::new(
239 step.action_context.clone(),
240 Progress::new(i, i, Some("steps".to_string())),
241 None,
242 )),
243 ));
244
245 if step.done {
246 interrupter.fail_on_error(processor.process_valid(
247 AgentEvaluationEvent::EpisodeEnd(EvaluationItem::new(
248 EpisodeSummary {
249 episode_length: step.ep_len,
250 cum_reward: step.cum_reward,
251 },
252 Progress::new(
253 episode_num + 1,
254 num_episodes,
255 Some("episodes".to_string()),
256 ),
257 None,
258 )),
259 ));
260 }
261 } else {
262 interrupter.fail_on_error(processor.process_train(RLEvent::EnvStep(
263 EvaluationItem::new(
264 step.action_context.clone(),
265 Progress::new(i, i, Some("steps".to_string())),
266 None,
267 ),
268 )));
269
270 if step.done {
271 interrupter.fail_on_error(processor.process_train(RLEvent::EpisodeEnd(
272 EvaluationItem::new(
273 EpisodeSummary {
274 episode_length: step.ep_len,
275 cum_reward: step.cum_reward,
276 },
277 Progress::new(
278 episode_num + 1,
279 num_episodes,
280 Some("episodes".to_string()),
281 ),
282 None,
283 ),
284 )));
285 }
286 }
287 }
288
289 items.push(trajectory);
290 if interrupter.should_stop() {
291 break;
292 }
293 }
294 self.agent.decrement_agents(1);
295 items
296 }
297
298 fn update_policy(&mut self, update: RLC::PolicyState) {
299 self.agent.update(update);
300 self.agent.clone().to_device(&self.device);
301 }
302
303 fn policy(&self) -> RLC::PolicyState {
304 self.agent.state()
305 }
306
307 fn device(&self) -> Device {
308 self.device.clone()
309 }
310}
311
312pub struct MultiAgentEnvLoop<RLC: RLComponentsTypes> {
314 num_envs: usize,
315 eval: bool,
316 agent: AsyncPolicy<RLC::Policy>,
317 transition_receiver: Receiver<RLTimeStep<RLC>>,
318 trajectory_receiver: Receiver<RLTrajectory<RLC>>,
319 request_senders: Vec<Sender<RequestMessage>>,
320 device: Device,
321}
322
323impl<RLC: RLComponentsTypes> MultiAgentEnvLoop<RLC> {
324 pub fn new(
326 num_envs: usize,
327 env_init: RLC::EnvInit,
328 agent: AsyncPolicy<RLC::Policy>,
329 eval: bool,
330 deterministic: bool,
331 device: &Device,
332 ) -> Self {
333 let (transition_sender, transition_receiver) = std::sync::mpsc::channel();
334 let (trajectory_sender, trajectory_receiver) = std::sync::mpsc::channel();
335 let mut request_senders = vec![];
336
337 agent.increment_agents(num_envs);
339
340 for i in 0..num_envs {
341 let config = AsyncAgentEnvLoopConfig {
342 eval,
343 deterministic,
344 id: i,
345 };
346 let runner = AgentEnvAsyncLoop::<RLC>::new(
347 env_init.clone(),
348 agent.clone(),
349 config,
350 &device.clone(),
351 Some(transition_sender.clone()),
352 Some(trajectory_sender.clone()),
353 );
354 request_senders.push(runner.request_sender.clone());
355 }
356
357 request_senders.iter().for_each(|s| {
359 s.send(RequestMessage::Step())
360 .expect("Main thread can send step requests.")
361 });
362
363 Self {
364 num_envs,
365 eval,
366 agent: agent.clone(),
367 transition_receiver,
368 trajectory_receiver,
369 request_senders,
370 device: device.clone(),
371 }
372 }
373}
374
375impl<RLC> AgentEnvLoop<RLC> for MultiAgentEnvLoop<RLC>
376where
377 RLC: RLComponentsTypes,
378{
379 fn run_steps(
380 &mut self,
381 num_steps: usize,
382 processor: &mut RLEventProcessorType<RLC>,
383 interrupter: &Interrupter,
384 progress: &mut Progress,
385 ) -> Vec<RLTimeStep<RLC>> {
386 let mut items = vec![];
387 for _ in 0..num_steps {
388 let transition = self
389 .transition_receiver
390 .recv()
391 .expect("Can receive transitions.");
392 items.push(transition.clone());
393
394 self.request_senders[transition.env_id]
395 .send(RequestMessage::Step())
396 .expect("Main thread can request steps.");
397
398 if !self.eval {
399 progress.items_processed += 1;
400 interrupter.fail_on_error(processor.process_train(RLEvent::EnvStep(
401 EvaluationItem::new(transition.action_context, progress.clone(), None),
402 )));
403
404 if transition.done {
405 interrupter.fail_on_error(processor.process_train(RLEvent::EpisodeEnd(
406 EvaluationItem::new(
407 EpisodeSummary {
408 episode_length: transition.ep_len,
409 cum_reward: transition.cum_reward,
410 },
411 progress.clone(),
412 None,
413 ),
414 )));
415 }
416 }
417
418 if interrupter.should_stop() {
419 break;
420 }
421 }
422 items
423 }
424
425 fn update_policy(&mut self, update: RLC::PolicyState) {
426 self.agent.update(update);
427 self.agent.clone().to_device(&self.device);
428 }
429
430 fn run_episodes(
431 &mut self,
432 num_episodes: usize,
433 processor: &mut RLEventProcessorType<RLC>,
434 interrupter: &Interrupter,
435 _progress: &mut Progress,
436 ) -> Vec<RLTrajectory<RLC>> {
437 let mut idx = vec![];
439 if num_episodes < self.num_envs {
440 let mut rng = rand::rng();
441 let mut vec: Vec<usize> = (0..self.num_envs).collect();
442 vec.shuffle(&mut rng);
443 idx = vec.into_iter().take(num_episodes).collect();
444 } else {
445 idx = (0..self.num_envs).collect();
446 }
447 let num_requests = self.num_envs.min(num_episodes);
448 idx.into_iter().for_each(|i| {
449 self.request_senders[i]
450 .send(RequestMessage::Episode())
451 .expect("Main thread can request steps.");
452 });
453
454 let mut items = vec![];
455 for episode_num in 0..num_episodes {
456 let trajectory = self
457 .trajectory_receiver
458 .recv()
459 .expect("Can receive trajectory.");
460 items.push(trajectory.clone());
461 if items.len() + num_requests <= num_episodes {
462 self.request_senders[trajectory.timesteps[0].env_id]
463 .send(RequestMessage::Episode())
464 .expect("Main thread can request steps.");
465 }
466 for (i, step) in trajectory.timesteps.iter().enumerate() {
467 if self.eval {
468 interrupter.fail_on_error(processor.process_valid(
469 AgentEvaluationEvent::EnvStep(EvaluationItem::new(
470 step.action_context.clone(),
471 Progress::new(i, i, Some("steps".to_string())),
472 None,
473 )),
474 ));
475
476 if step.done {
477 interrupter.fail_on_error(processor.process_valid(
478 AgentEvaluationEvent::EpisodeEnd(EvaluationItem::new(
479 EpisodeSummary {
480 episode_length: step.ep_len,
481 cum_reward: step.cum_reward,
482 },
483 Progress::new(
484 episode_num + 1,
485 num_episodes,
486 Some("episodes".to_string()),
487 ),
488 None,
489 )),
490 ));
491 }
492 } else {
493 interrupter.fail_on_error(processor.process_train(RLEvent::EnvStep(
494 EvaluationItem::new(
495 step.action_context.clone(),
496 Progress::new(i, i, Some("steps".to_string())),
497 None,
498 ),
499 )));
500
501 if step.done {
502 interrupter.fail_on_error(processor.process_train(RLEvent::EpisodeEnd(
503 EvaluationItem::new(
504 EpisodeSummary {
505 episode_length: step.ep_len,
506 cum_reward: step.cum_reward,
507 },
508 Progress::new(
509 episode_num + 1,
510 num_episodes,
511 Some("episodes".to_string()),
512 ),
513 None,
514 ),
515 )));
516 }
517 }
518 }
519
520 if interrupter.should_stop() {
521 break;
522 }
523 }
524
525 items
526 }
527
528 fn policy(&self) -> RLC::PolicyState {
529 self.agent.state()
530 }
531
532 fn device(&self) -> Device {
533 self.device.clone()
534 }
535}
536
537#[cfg(test)]
538#[allow(clippy::needless_range_loop)]
539mod tests {
540 use burn_core::data::dataloader::Progress;
541 use burn_rl::AsyncPolicy;
542
543 use crate::learner::rl::env_runner::async_runner::AsyncAgentEnvLoopConfig;
544 use crate::learner::rl::env_runner::base::AgentEnvLoop;
545 use crate::learner::tests::{MockPolicyState, MockProcessor};
546 use crate::{
547 AgentEnvAsyncLoop,
548 learner::tests::{MockEnvInit, MockPolicy, MockRLComponents},
549 };
550 use crate::{AsyncProcessorTraining, Interrupter, MultiAgentEnvLoop};
551
552 fn setup_async_loop(
553 state: usize,
554 eval: bool,
555 deterministic: bool,
556 ) -> AgentEnvAsyncLoop<MockRLComponents> {
557 let env_init = MockEnvInit;
558 let agent = MockPolicy(state);
559 let config = AsyncAgentEnvLoopConfig {
560 eval,
561 deterministic,
562 id: 0,
563 };
564 AgentEnvAsyncLoop::<MockRLComponents>::new(
565 env_init,
566 AsyncPolicy::new(1, agent),
567 config,
568 &Default::default(),
569 None,
570 None,
571 )
572 }
573
574 fn setup_multi_loop(
575 num_envs: usize,
576 autobatch_size: usize,
577 state: usize,
578 eval: bool,
579 deterministic: bool,
580 ) -> MultiAgentEnvLoop<MockRLComponents> {
581 let env_init = MockEnvInit;
582 let agent = MockPolicy(state);
583 MultiAgentEnvLoop::<MockRLComponents>::new(
584 num_envs,
585 env_init,
586 AsyncPolicy::new(autobatch_size, agent),
587 eval,
588 deterministic,
589 &Default::default(),
590 )
591 }
592
593 #[test]
594 fn test_policy_async_loop() {
595 let runner = setup_async_loop(1000, false, false);
596 let policy_state = runner.policy();
597 assert_eq!(policy_state.0, 1000);
598 }
599
600 #[test]
601 fn test_update_policy_async_loop() {
602 let mut runner = setup_async_loop(0, false, false);
603
604 runner.update_policy(MockPolicyState(1));
605 assert_eq!(runner.policy().0, 1);
606 }
607
608 #[test]
609 fn run_steps_returns_requested_number_async_loop() {
610 let mut runner = setup_async_loop(0, false, false);
611 let mut processor = AsyncProcessorTraining::new(MockProcessor);
612 let interrupter = Interrupter::new();
613 let mut progress = Progress {
614 items_processed: 0,
615 items_total: 1,
616 unit: Some("steps".to_string()),
617 };
618
619 let steps = runner.run_steps(1, &mut processor, &interrupter, &mut progress);
620 assert_eq!(steps.len(), 1);
621 let steps = runner.run_steps(8, &mut processor, &interrupter, &mut progress);
622 assert_eq!(steps.len(), 8);
623 }
624
625 #[test]
626 fn run_episodes_returns_requested_number_async_loop() {
627 let mut runner = setup_async_loop(0, false, false);
628 let mut processor = AsyncProcessorTraining::new(MockProcessor);
629 let interrupter = Interrupter::new();
630 let mut progress = Progress {
631 items_processed: 0,
632 items_total: 1,
633 unit: Some("steps".to_string()),
634 };
635
636 let trajectories = runner.run_episodes(1, &mut processor, &interrupter, &mut progress);
637 assert_eq!(trajectories.len(), 1);
638 assert_ne!(trajectories[0].timesteps.len(), 0);
639 let trajectories = runner.run_episodes(8, &mut processor, &interrupter, &mut progress);
640 assert_eq!(trajectories.len(), 8);
641 for i in 0..8 {
642 assert_ne!(trajectories[i].timesteps.len(), 0);
643 }
644 }
645
646 #[test]
647 fn test_policy_multi_loop() {
648 let runner = setup_multi_loop(4, 4, 1000, false, false);
649 let policy_state = runner.policy();
650 assert_eq!(policy_state.0, 1000);
651 }
652
653 #[test]
654 fn test_update_policy_multi_loop() {
655 let mut runner = setup_multi_loop(4, 4, 0, false, false);
656
657 runner.update_policy(MockPolicyState(1));
658 assert_eq!(runner.policy().0, 1);
659 }
660
661 #[test]
662 fn run_steps_returns_requested_number_multi_loop() {
663 fn run_test(num_envs: usize, autobatch_size: usize) {
664 let mut runner = setup_multi_loop(num_envs, autobatch_size, 0, false, false);
665 let mut processor = AsyncProcessorTraining::new(MockProcessor);
666 let interrupter = Interrupter::new();
667 let mut progress = Progress {
668 items_processed: 0,
669 items_total: 1,
670 unit: Some("steps".to_string()),
671 };
672
673 let steps = runner.run_steps(8, &mut processor, &interrupter, &mut progress);
675 assert_eq!(steps.len(), 8);
676
677 for i in 0..16 {
678 let steps = runner.run_steps(i, &mut processor, &interrupter, &mut progress);
679 assert_eq!(steps.len(), i);
680 }
681 }
682
683 run_test(1, 1);
685 run_test(4, 4);
686 run_test(1, 2);
688 run_test(1, 3);
689 run_test(2, 3);
690 run_test(2, 4);
691 run_test(5, 19);
692 run_test(2, 1);
694 run_test(8, 1);
695 run_test(3, 2);
696 run_test(8, 2);
697 run_test(8, 3);
698 run_test(8, 7);
699 }
700
701 #[test]
702 fn run_episodes_returns_requested_number_multi_loop() {
703 fn run_test(num_envs: usize, autobatch_size: usize) {
704 let mut runner = setup_multi_loop(num_envs, autobatch_size, 0, false, false);
705 let mut processor = AsyncProcessorTraining::new(MockProcessor);
706 let interrupter = Interrupter::new();
707 let mut progress = Progress {
708 items_processed: 0,
709 items_total: 1,
710 unit: Some("steps".to_string()),
711 };
712
713 let trajectories = runner.run_episodes(8, &mut processor, &interrupter, &mut progress);
715 assert_eq!(trajectories.len(), 8);
716 for j in 0..8 {
717 assert_ne!(trajectories[j].timesteps.len(), 0);
718 }
719
720 for i in 0..16 {
721 let trajectories =
722 runner.run_episodes(i, &mut processor, &interrupter, &mut progress);
723 assert_eq!(trajectories.len(), i);
724 for j in 0..i {
725 assert_ne!(trajectories[j].timesteps.len(), 0);
726 }
727 }
728 }
729
730 run_test(1, 1);
732 run_test(4, 4);
733 run_test(1, 2);
735 run_test(1, 3);
736 run_test(2, 3);
737 run_test(2, 4);
738 run_test(5, 19);
739 run_test(2, 1);
741 run_test(8, 1);
742 run_test(3, 2);
743 run_test(8, 2);
744 run_test(8, 3);
745 run_test(8, 7);
746 }
747}