use burn_tensor::backend::Backend;
use burn_tensor::{BasicOps, Float, Tensor, TensorKind};
#[cfg(feature = "tch-backend")]
use relayrl_types::data::tensor::TchDType;
use relayrl_types::data::tensor::{DType, NdArrayDType};
use relayrl_types::prelude::tensor::relayrl::{BackendMatcher, SupportedTensorBackend};
use super::error::NeuralNetworkError;
use super::generic_mlp::GenericMlp;
use super::traits::{NeuralNetwork, NeuralNetworkForward, NeuralNetworkSpec, WeightProvider};
use super::types::{ActivationKind, LayerSpecs};
#[derive(Clone, Debug)]
pub struct ValueFunction<
B: Backend + BackendMatcher<Backend = B>,
KindIn: TensorKind<B> + BasicOps<B>,
>(GenericMlp<B, KindIn, Float>);
impl<B: Backend + BackendMatcher<Backend = B>, KindIn: TensorKind<B> + BasicOps<B>>
ValueFunction<B, KindIn>
{
pub fn new(vf_mlp: GenericMlp<B, KindIn, Float>) -> Result<Self, NeuralNetworkError> {
match (vf_mlp.output_dtype(), vf_mlp.output_dim()) {
(DType::NdArray(NdArrayDType::F32), 1) => Ok(Self(vf_mlp)),
#[cfg(feature = "tch-backend")]
(DType::Tch(TchDType::F32), 1) => Ok(Self(vf_mlp)),
_ => Err(NeuralNetworkError::UnsupportedOutputParams(
vf_mlp.output_dtype().to_string(),
vf_mlp.output_dim().to_string(),
)),
}
}
pub fn new_generic_mlp(
input_dim: usize,
input_dtype: DType,
hidden_sizes: &[usize],
activation: ActivationKind<B>,
device: &B::Device,
) -> Result<Self, NeuralNetworkError> {
let output_dype: DType = match B::get_supported_backend() {
SupportedTensorBackend::NdArray => DType::NdArray(NdArrayDType::F32),
#[cfg(feature = "tch-backend")]
SupportedTensorBackend::Tch => DType::Tch(TchDType::F32),
_ => {
return Err(NeuralNetworkError::BackendUnavailable(
match B::get_supported_backend() {
SupportedTensorBackend::NdArray => "NdArray",
#[cfg(feature = "tch-backend")]
SupportedTensorBackend::Tch => "Tch",
_ => "None",
}
.to_string(),
));
}
};
Self::new(GenericMlp::new(
input_dim,
input_dtype,
hidden_sizes,
1,
output_dype,
activation,
device,
))
}
pub fn new_default_mlp(
input_dim: usize,
input_dtype: DType,
device: &B::Device,
) -> Result<Self, NeuralNetworkError> {
Ok(Self(GenericMlp::default(
input_dim,
input_dtype,
1,
DType::NdArray(NdArrayDType::F32),
device,
)))
}
pub fn get_vf_layer_specs(&self) -> LayerSpecs {
self.0.get_layer_specs()
}
}
impl<
B: Backend + BackendMatcher<Backend = B>,
KindIn: TensorKind<B> + BasicOps<B>,
KindOut: TensorKind<B> + BasicOps<B>,
> NeuralNetworkSpec<B, KindIn, KindOut> for ValueFunction<B, KindIn>
{
fn input_dim(&self) -> &usize {
self.0.input_dim()
}
fn input_dtype(&self) -> &DType {
self.0.input_dtype()
}
fn output_dim(&self) -> &usize {
self.0.output_dim()
}
fn output_dtype(&self) -> &DType {
self.0.output_dtype()
}
}
impl<B: Backend + BackendMatcher<Backend = B>, KindIn: TensorKind<B> + BasicOps<B>>
NeuralNetworkForward<B, KindIn, Float> for ValueFunction<B, KindIn>
where
KindIn: BasicOps<B>,
{
fn forward<const IN_D: usize, const OUT_D: usize>(
&self,
input: Tensor<B, IN_D, KindIn>,
) -> Tensor<B, OUT_D, Float>
where
KindIn: BasicOps<B>,
{
self.0.forward(input)
}
}