use std::sync::{Arc, Mutex};
use itertools::izip;
use serde::{Deserialize, Serialize};
use crate::{error::TensorError, running_mean::RunningMeanStd, tensor::R2lTensor};
type Result<T> = std::result::Result<T, TensorError>;
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum NormalizerMode {
Update,
ReadOnly,
}
const EPSILON: f32 = 1e-8;
#[derive(Clone)]
struct ClippedRunningMean<T: R2lTensor> {
rm: RunningMeanStd<T>,
clip: f32,
}
impl<T: R2lTensor> ClippedRunningMean<T> {
pub fn update(&mut self, obs: &[T]) -> Result<()> {
self.rm.update(obs)
}
pub fn normalize_in_place(&self, obs: &mut [T]) -> Result<()> {
let mean = self.rm.mean.to_vec()?;
let var = self.rm.var.to_vec()?;
for obs in obs {
let (data, shape) = obs.to_vec_and_shape()?;
let normalized = izip!(data, &mean, &var)
.map(|(val, mean, var)| {
((val - mean) / (var + EPSILON).sqrt()).clamp(-self.clip, self.clip)
})
.collect();
*obs = T::from_vec_and_shape(normalized, shape)?;
}
Ok(())
}
}
struct ClippedNormalizerInner<T: R2lTensor>(Arc<Mutex<ClippedRunningMean<T>>>);
impl<T: R2lTensor> Clone for ClippedNormalizerInner<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
#[derive(Clone)]
pub struct ClippedNormalizer<T: R2lTensor> {
normalizer_mode: NormalizerMode,
inner: ClippedNormalizerInner<T>,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ClippedNormalizerSnapshot {
normalizer_mode: NormalizerMode,
obs_shape: Vec<usize>,
mean: Vec<f32>,
var: Vec<f32>,
count: f32,
clip: f32,
}
impl<T: R2lTensor> ClippedNormalizer<T> {
#[must_use]
pub fn with_mode(&self, normalizer_mode: NormalizerMode) -> Self {
Self {
normalizer_mode,
inner: self.inner.clone(),
}
}
pub fn new(normalizer_mode: NormalizerMode, rm: RunningMeanStd<T>, clip: f32) -> Self {
let inner = ClippedRunningMean { rm, clip };
Self {
normalizer_mode,
inner: ClippedNormalizerInner(Arc::new(Mutex::new(inner))),
}
}
pub fn build(normalizer_mode: NormalizerMode, clip: f32, shape: Vec<usize>) -> Result<Self> {
let rm = RunningMeanStd::new(shape)?;
let inner = ClippedRunningMean { rm, clip };
Ok(Self {
normalizer_mode,
inner: ClippedNormalizerInner(Arc::new(Mutex::new(inner))),
})
}
pub fn apply_slice_in_place(&self, obs: &mut [T]) -> Result<()> {
let mut inner = self.inner.0.lock().unwrap();
match self.normalizer_mode {
NormalizerMode::ReadOnly => inner.normalize_in_place(obs),
NormalizerMode::Update => {
inner.update(obs)?;
inner.normalize_in_place(obs)
}
}
}
pub fn apply_tensor_in_place(&self, obs: &mut T) -> Result<()> {
self.apply_slice_in_place(std::slice::from_mut(obs))
}
pub fn snapshot(&self) -> Result<ClippedNormalizerSnapshot> {
let inner = self.inner.0.lock().unwrap();
let (mean, obs_shape) = inner.rm.mean.to_vec_and_shape()?;
Ok(ClippedNormalizerSnapshot {
normalizer_mode: self.normalizer_mode,
obs_shape,
mean,
var: inner.rm.var.to_vec()?,
count: inner.rm.count,
clip: inner.clip,
})
}
}
impl ClippedNormalizerSnapshot {
pub fn into_normalizer<T: R2lTensor>(self) -> Result<ClippedNormalizer<T>> {
let mean = T::from_vec_and_shape(self.mean, self.obs_shape.clone())?;
let var = T::from_vec_and_shape(self.var, self.obs_shape)?;
let rm = RunningMeanStd::build(mean, var, self.count);
Ok(ClippedNormalizer::new(self.normalizer_mode, rm, self.clip))
}
}