use std::{
collections::BTreeMap,
fmt::Debug,
ops::{Deref, DerefMut},
};
use burn::tensor::{backend::Backend, Tensor, TensorKind};
use crate::util::summary_from_keys;
pub trait Environment {
type State: Clone + Debug;
type Action: Clone + Debug;
fn step(&mut self, action: Self::Action) -> (Option<Self::State>, f32);
fn reset(&mut self) -> Self::State;
fn random_action(&self) -> Self::Action;
fn is_active(&self) -> bool {
true
}
}
pub trait DiscreteActionSpace: Environment {
fn actions(&self) -> Vec<Self::Action>;
}
pub trait ToTensor<B: Backend, const D: usize, K: TensorKind<B>> {
fn to_tensor(self, device: &B::Device) -> Tensor<B, D, K>;
}
#[derive(Debug)]
pub struct Report {
keys: Vec<&'static str>,
map: BTreeMap<&'static str, f64>,
}
impl Report {
pub fn new(mut keys: Vec<&'static str>) -> Self {
keys.sort_unstable();
let map = summary_from_keys(&keys);
Self { keys, map }
}
pub fn keys(&self) -> &[&'static str] {
&self.keys
}
pub fn take(&mut self) -> BTreeMap<&'static str, f64> {
std::mem::replace(&mut self.map, summary_from_keys(&self.keys))
}
}
impl Deref for Report {
type Target = BTreeMap<&'static str, f64>;
fn deref(&self) -> &Self::Target {
&self.map
}
}
impl DerefMut for Report {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.map
}
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
pub(crate) struct MockEnv;
impl Environment for MockEnv {
type State = i32;
type Action = i32;
fn step(&mut self, _action: Self::Action) -> (Option<Self::State>, f32) {
(None, 0.0)
}
fn reset(&mut self) -> Self::State {
0
}
fn random_action(&self) -> Self::Action {
0
}
}
#[test]
fn report_functional() {
let mut report = Report::new(vec!["c", "a", "b"]);
assert_eq!(
*report.keys(),
["a", "b", "c"],
"Keys were sorted on initialization"
);
report.entry("a").and_modify(|x| *x += 1.0);
assert_eq!(
*report.get("a").unwrap(),
1.0,
"Mutations on entries work and report derefs into inner map"
);
let inner_map = report.take();
assert!(
inner_map.values().eq([1.0, 0.0, 0.0].iter()),
"Inner map can be taken with correct values"
);
assert!(
report.values().eq([0.0, 0.0, 0.0].iter()),
"Taking inner map leaves default values in report"
);
}
}