use crate::{
HookResult, break_on_hook_result,
buffers::{TrajectoryBatch, buffer::TrajectoryView},
error::Error,
models::Actor,
return_on_hook_result,
tensor::R2lTensor,
utils::{actor_wrapper::ActorWrapper, buffer_wrapper::TrajectoryViewsWrapper},
};
pub trait Agent {
type Tensor: R2lTensor;
type Actor: Actor<Tensor = Self::Tensor> + Clone;
fn actor(&self) -> Self::Actor;
fn learn<B: TrajectoryBatch<Self::Tensor>>(&mut self, buffers: &[B]) -> Result<(), Error>;
fn set_learning_rate(&mut self, learning_rate: f64);
}
pub trait Sampler {
type Tensor: R2lTensor;
fn reset_all_envs(&mut self) -> Result<(), Error> {
Ok(())
}
fn collect_rollouts<A: Actor<Tensor = Self::Tensor> + Clone>(
&mut self,
actor: A,
) -> Result<(), Error>;
fn trajectory_views(&mut self) -> impl AsRef<[TrajectoryView<'_, Self::Tensor>]>;
}
pub struct OnPolicyRuntime<A: Agent, S: Sampler> {
pub agent: A,
pub sampler: S,
}
impl<A: Agent, S: Sampler> OnPolicyRuntime<A, S> {
pub fn collect(&mut self) -> Result<(), Error> {
let actor = self.agent.actor();
let actor = ActorWrapper::new(actor);
self.sampler.collect_rollouts(actor)
}
pub fn trajectory_containers(&mut self) -> impl AsRef<[TrajectoryView<'_, S::Tensor>]> {
self.sampler.trajectory_views()
}
pub fn learn(&mut self) -> Result<(), Error> {
let views = self.sampler.trajectory_views();
let buffers = views
.as_ref()
.iter()
.map(TrajectoryViewsWrapper::from_view)
.collect::<Result<Vec<_>, _>>()?;
self.agent.learn(&buffers)
}
pub fn actor(&self) -> A::Actor {
self.agent.actor()
}
}
pub trait OnPolicyAlgorithmHooks {
type A: Agent;
type S: Sampler;
fn init_hook(&mut self, runtime: &mut OnPolicyRuntime<Self::A, Self::S>) -> HookResult;
fn post_rollout_hook(&mut self, runtime: &mut OnPolicyRuntime<Self::A, Self::S>) -> HookResult;
fn post_training_hook(&mut self, runtime: &mut OnPolicyRuntime<Self::A, Self::S>)
-> HookResult;
fn finish_training_hook(
&mut self,
runtime: &mut OnPolicyRuntime<Self::A, Self::S>,
) -> Result<(), Error>;
}
pub struct OnPolicyAlgorithm<A: Agent, S: Sampler, H: OnPolicyAlgorithmHooks<A = A, S = S>> {
pub runtime: OnPolicyRuntime<A, S>,
pub hooks: H,
}
impl<A: Agent, S: Sampler, H: OnPolicyAlgorithmHooks<A = A, S = S>> OnPolicyAlgorithm<A, S, H> {
fn training_loop(&mut self) -> Result<(), Error> {
return_on_hook_result!(self.hooks.init_hook(&mut self.runtime));
loop {
self.runtime.collect()?;
break_on_hook_result!(self.hooks.post_rollout_hook(&mut self.runtime));
self.runtime.learn()?;
break_on_hook_result!(self.hooks.post_training_hook(&mut self.runtime));
}
Ok(())
}
}
impl<A: Agent, S: Sampler, H: OnPolicyAlgorithmHooks<A = A, S = S>> OnPolicyAlgorithm<A, S, H> {
pub fn new(runtime: OnPolicyRuntime<A, S>, hooks: H) -> Self {
Self { runtime, hooks }
}
pub fn train(&mut self) -> Result<(), Error> {
let training_result = self.training_loop();
let finalization_result = self.hooks.finish_training_hook(&mut self.runtime);
training_result.and(finalization_result)
}
}