use crate::{
buffers::{Memory, TrajectoryBatch},
tensor::R2lTensor,
};
#[derive(Clone)]
pub struct TrajectoryBuffer<T: R2lTensor> {
states: Vec<T>,
next_states: Vec<T>,
actions: Vec<T>,
rewards: Vec<f32>,
terminated: Vec<bool>,
truncated: Vec<bool>,
}
impl<T: R2lTensor> Default for TrajectoryBuffer<T> {
fn default() -> Self {
Self {
states: Vec::default(),
next_states: Vec::default(),
actions: Vec::default(),
rewards: Vec::default(),
terminated: Vec::default(),
truncated: Vec::default(),
}
}
}
impl<T: R2lTensor> TrajectoryBuffer<T> {
pub fn clear(&mut self) {
self.states.clear();
self.next_states.clear();
self.actions.clear();
self.rewards.clear();
self.terminated.clear();
self.truncated.clear();
}
pub fn push(&mut self, memory: Memory<T>) {
let Memory {
state,
next_state,
action,
reward,
terminated,
truncated,
} = memory;
self.states.push(state);
self.next_states.push(next_state);
self.actions.push(action);
self.rewards.push(reward);
self.terminated.push(terminated);
self.truncated.push(truncated);
}
pub fn replace_last_next_state(&mut self, next_state: T) {
if let Some(last_next_state) = self.next_states.last_mut() {
*last_next_state = next_state;
}
}
#[must_use]
pub fn len(&self) -> usize {
self.states.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.states.is_empty()
}
#[must_use]
pub fn terminated(&self) -> &[bool] {
&self.terminated
}
#[must_use]
pub fn truncated(&self) -> &[bool] {
&self.truncated
}
#[must_use]
pub fn rewards(&self) -> &[f32] {
&self.rewards
}
pub fn rewards_mut(&mut self) -> &mut [f32] {
&mut self.rewards
}
#[must_use]
pub fn to_trajectory_view(&self) -> TrajectoryView<'_, T> {
TrajectoryView {
states: &self.states,
next_states: &self.next_states,
actions: &self.actions,
rewards: &self.rewards,
terminated: &self.terminated,
truncated: &self.truncated,
}
}
}
pub struct TrajectoryView<'a, T: R2lTensor> {
pub states: &'a [T],
pub next_states: &'a [T],
pub actions: &'a [T],
pub rewards: &'a [f32],
pub terminated: &'a [bool],
pub truncated: &'a [bool],
}
impl<T: R2lTensor> TrajectoryBatch<T> for TrajectoryView<'_, T> {
fn len(&self) -> usize {
self.states.len()
}
fn is_empty(&self) -> bool {
self.states.is_empty()
}
fn states(&self) -> &[T] {
self.states
}
fn next_states(&self) -> &[T] {
self.next_states
}
fn actions(&self) -> &[T] {
self.actions
}
fn rewards(&self) -> &[f32] {
self.rewards
}
fn terminated(&self) -> &[bool] {
self.terminated
}
fn truncated(&self) -> &[bool] {
self.truncated
}
}
impl<T: R2lTensor> TrajectoryView<'_, T> {
pub fn dones(&self) -> impl Iterator<Item = bool> {
self.terminated
.iter()
.zip(self.truncated.iter())
.map(|(terminated, truncated)| *terminated || *truncated)
}
#[must_use]
pub fn episode_terminations(&self) -> usize {
self.dones().filter(|x| *x).count()
}
}