use rayon::prelude::*;
use rayon::ThreadPoolBuilder;
use anyhow::Result;
use crate::rl::env::Env;
use crate::nn::policy::{Policy, sample};
use crate::collector::collector::{CollectedData, Collector, merge};
use crate::rl::search::predict_probs_mcts;
#[derive(Clone)]
pub struct AZCollector {
pub num_episodes: usize,
pub num_mcts_searches: usize,
pub C: f32,
pub max_expand_depth: usize,
pub num_cores: usize,
}
impl AZCollector {
pub fn new(
num_episodes: usize,
num_mcts_searches: usize,
C: f32,
max_expand_depth: usize,
num_cores: usize,
) -> Self {
AZCollector {
num_episodes,
num_mcts_searches,
C,
max_expand_depth,
num_cores,
}
}
}
impl AZCollector {
fn single_collect(
&self,
env: &Box<dyn Env>,
policy: &Policy,
) -> CollectedData {
let mut env = env.clone();
env.reset();
let mut obs: Vec<Vec<usize>> = vec![];
let mut probs: Vec<Vec<f32>> = vec![];
let mut vals: Vec<f32> = vec![];
let mut total_vals: Vec<f32> = vec![];
let mut total_val = 0.0;
loop {
let mcts_probs = predict_probs_mcts(env.clone(), policy, self.num_mcts_searches, self.C, self.max_expand_depth);
let action = sample(&mcts_probs);
let val = env.reward();
total_vals.push(total_val);
total_val += val;
obs.push(env.observe());
probs.push(mcts_probs);
vals.push(val);
if env.is_final() {
break;
}
env.step(action);
}
let remaining_vals: Vec<f32> = total_vals.iter().map(|&v| total_val - v).collect();
let perms: Vec<Option<usize>> = vec![None; obs.len()];
let mut data = CollectedData::new(
obs,
probs,
perms,
vec![],
vec![],
vec![],
);
data.additional_data
.insert("remaining_values".into(), remaining_vals);
data
}
}
impl Collector for AZCollector {
fn collect(&self, env: &Box<dyn Env>, policy: &Policy) -> Result<CollectedData> {
if self.num_cores == 1 {
Ok(merge(
(0..self.num_episodes).into_iter()
.map(|_| self.single_collect(env, policy))
.collect()
)?)
} else {
let pool: rayon::ThreadPool = ThreadPoolBuilder::new().num_threads(self.num_cores).build()?;
Ok(merge(pool.install(|| {
(0..self.num_episodes).into_par_iter() .map(|_| self.single_collect(env, policy))
.collect()
}))?)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nn::layers::{EmbeddingBag, Linear};
use crate::nn::modules::Sequential;
use crate::nn::policy::Policy;
use crate::rl::env::Env;
#[derive(Clone)]
struct DummyEnv { step: usize }
impl DummyEnv {
fn new() -> Self { Self { step: 0 } }
}
impl Env for DummyEnv {
fn as_any(&self) -> &dyn std::any::Any { self }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
fn num_actions(&self) -> usize { 1 }
fn obs_shape(&self) -> Vec<usize> { vec![1] }
fn set_state(&mut self, state: Vec<i64>) { self.step = state[0] as usize; }
fn reset(&mut self) { self.step = 0; }
fn step(&mut self, _action: usize) { self.step += 1; }
fn masks(&self) -> Vec<bool> { vec![true] }
fn is_final(&self) -> bool { self.step >= 1 }
fn reward(&self) -> f32 { 1.0 }
fn observe(&self) -> Vec<usize> { vec![0] }
fn success(&self) -> bool { true }
}
fn dummy_policy() -> Policy {
let emb = EmbeddingBag::new(vec![vec![1.0]], vec![0.0], false, vec![1], 0);
let lin = Linear::new(vec![1.0], vec![0.0], false);
let seq_a = Sequential::new(vec![Box::new(lin.clone())]);
let seq_v = Sequential::new(vec![Box::new(lin)]);
Policy::new(
Box::new(emb),
Box::new(Sequential::new(vec![])),
Box::new(seq_a),
Box::new(seq_v),
vec![],
vec![],
)
}
#[test]
fn test_azcollector_collect() {
let env: Box<dyn Env> = Box::new(DummyEnv::new());
let policy = dummy_policy();
let collector = AZCollector::new(1, 1, 1.0, 1, 1);
let data = collector.collect(&env, &policy).unwrap();
assert_eq!(data.obs.len(), 2);
assert!(data.additional_data.contains_key("remaining_values"));
}
}