use std::fmt::Debug;
use crate::{algs::AlgorithmError, utils::{dim_product, encode_mixed_radix, IntoF32}};
use super::algs::Result;
use candle_core::{Device, Tensor};
#[derive(Debug, Clone)]
pub enum QValue<T = u16> {
Deterministic(Action<T>),
Stochastic(Vec<(Action<T>, f32)>),
}
impl<T> QValue<T> {
pub fn try_best_action(&self) -> Option<&Action<T>> {
match self {
QValue::Deterministic(action) => Some(action),
QValue::Stochastic(actions_with_values) => {
if actions_with_values.is_empty() {
None
} else {
actions_with_values.iter()
.max_by(|(_, val1), (_, val2)| val1.total_cmp(val2))
.map(|(action, _)| action)
}
},
}
}
pub fn best_action(&self) -> &Action<T> {
self.try_best_action().expect("No action available in QValue")
}
}
#[derive(Debug, Clone)]
pub struct Action<T = u16> {
pub(crate) value: Vec<T>,
pub(crate) uppers: Vec<T>,
}
impl<T> Action<T> {
pub fn new(value: Vec<T>, uppers: Vec<T>) -> Self {
Self { value, uppers }
}
pub fn as_slice(&self) -> &[T] {
&self.value
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.value
}
pub fn dim(&self) -> usize {
self.value.len()
}
pub fn upper_bound(&self) -> &[T] {
&self.uppers
}
}
impl<T> Action<T>
where
T: Copy + rand::distr::uniform::SampleUniform + Default + std::cmp::PartialOrd
{
pub fn random(&self, rng: &mut impl rand::Rng) -> Self {
let mut value = self.value.clone();
for (v, &up) in value.iter_mut().zip(&self.uppers) {
*v = rng.random_range(T::default()..up);
}
Self::new(value, self.uppers.clone())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Status<T = f32> {
pub(crate) value: Vec<T>,
pub(crate) uppers: Vec<T>,
}
impl<T: Clone + Eq + std::hash::Hash> std::hash::Hash for Status<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
impl<T> Status<T> {
pub fn new(values: Vec<T>, uppers: Vec<T>) -> Self {
Self { value: values, uppers }
}
pub fn as_slice(&self) -> &[T] {
&self.value
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.value
}
pub fn to_vec(&self) -> Vec<T>
where
T: Clone,
{
self.value.clone()
}
pub fn len(&self) -> usize {
self.value.len()
}
pub fn is_empty(&self) -> bool {
self.value.is_empty()
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Reward(pub f32);
#[derive(Clone, Debug)]
pub struct Sample<S: Clone = u16, A: Clone = u16> {
pub state: Status<S>,
pub action: Action<A>,
pub reward: Reward,
pub next_state: Status<S>,
pub done: bool,
}
impl<T: Clone + IntoF32> Action<T> {
pub fn to_tensor(&self, device: &Device) -> Result<Tensor> {
let values: Vec<f32> = self.as_slice()
.iter()
.map(|a| a.clone().into_f32())
.collect::<Result<Vec<f32>>>()?;
let len = values.len();
Ok(Tensor::from_vec(values, (1, len), device)?)
}
}
impl<T> Action<T> {
pub fn from_actions<A>(action: Action<A>) -> Result<Self>
where
A: TryInto<T> + Clone,
{
Ok(Self::new(
action.value.into_iter()
.map(|v| v.try_into().map_err(|_| AlgorithmError::InvalidParameters("动作转换为T失败".to_string())))
.collect::<Result<Vec<T>>>()?,
action.uppers.into_iter()
.map(|v| v.try_into().map_err(|_| AlgorithmError::InvalidParameters("上界转换为T失败".to_string())))
.collect::<Result<Vec<T>>>()?,
))
}
}
impl<T: Copy + IntoF32> Status<T> {
pub fn to_tensor(&self, device: &Device) -> Result<Tensor> {
let values: Vec<f32> = self.as_slice()
.iter()
.map(|&s| s.into_f32())
.collect::<Result<Vec<f32>>>()?;
let len = values.len();
Ok(Tensor::from_vec(values, len, device)?)
}
pub fn to_tensor_normalized(&self, uppers: &[T], device: &Device) -> Result<Tensor>
{
let values: Vec<f32> = self.as_slice()
.iter()
.zip(uppers)
.map(|(&s, &up)| {
let s_f32 = s.into_f32().map_err(|_| AlgorithmError::InvalidParameters("状态值转换为f32失败".to_string()))?;
let up_f32 = up.into_f32().map_err(|_| AlgorithmError::InvalidParameters("上界值转换为f32失败".to_string()))?;
if up_f32 == 0.0 {
return Err(AlgorithmError::InvalidParameters("上界值不能为0,无法进行归一化".to_string()));
}
Ok(s_f32 / up_f32)
})
.collect::<Result<Vec<f32>>>()?;
let len = values.len();
Ok(Tensor::from_vec(values, len, device)?)
}
pub fn to_one_hot_flat(&self, uppers: &[T], device: &Device) -> Result<Tensor>
where
T: Copy + TryInto<usize> + TryFrom<usize> + std::iter::Product,
{
let dim = dim_product(uppers)?;
let index = encode_mixed_radix(&self.value, uppers)?;
let one_hot = Tensor::new(
(0..dim).map(|i| if i == index { 1.0 } else { 0.0 }).collect::<Vec<f32>>(),
device,
)?;
Ok(one_hot)
}
}
impl<T> Status<T> {
pub fn from_status<S>(status: Status<S>) -> Result<Self>
where
S: TryInto<T> + Clone,
{
Ok(Self::new(
status.value.into_iter()
.map(|v| v.try_into().map_err(|_| AlgorithmError::InvalidParameters("状态转换为T失败".to_string())))
.collect::<Result<Vec<T>>>()?,
status.uppers.into_iter()
.map(|v| v.try_into().map_err(|_| AlgorithmError::InvalidParameters("上界转换为T失败".to_string())))
.collect::<Result<Vec<T>>>()?,
))
}
}
impl Reward {
pub fn to_tensor(&self, device: &Device) -> Result<Tensor> {
Ok(Tensor::from_slice(&[self.0], (1, 1), device)?)
}
}
pub trait EnvTrait<S: Clone = u16, A: Clone = u16> {
fn step(&mut self, state: &Status<S>, action: &Action<A>) -> (Status<S>, Reward, bool);
fn reset(&mut self) -> Status<S>;
fn action_space(&self) -> &[A];
fn state_space(&self) -> &[S];
fn as_any(&self) -> &dyn std::any::Any;
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
}