use crate::error::Error;
use crate::neural_network::layers::ParamCounts;
use crate::neural_network::layers::upsampling::resize_engine::{
upsample_backward, upsample_forward, upsample_output_shape, validate_factors,
};
use crate::neural_network::layers::upsampling::{Factor2D, Interpolation};
use crate::neural_network::layers::validation::start_build;
use crate::neural_network::layers::{
built_layer_shape_functions, no_trainable_parameters_layer_functions,
};
use crate::neural_network::traits::{LayerBase, UnaryLayer};
use crate::neural_network::{Ctx, Shape, Tensor};
#[derive(Debug)]
pub struct UpSampling2D {
size: Factor2D,
interpolation: Interpolation,
built: Option<Shape>,
}
impl UpSampling2D {
pub fn new(size: impl Into<Factor2D>, interpolation: Interpolation) -> Result<Self, Error> {
let size = size.into();
validate_factors(&size.0)?;
Ok(UpSampling2D {
size,
interpolation,
built: None,
})
}
}
impl LayerBase for UpSampling2D {
fn layer_type(&self) -> &str {
"UpSampling2D"
}
built_layer_shape_functions!();
no_trainable_parameters_layer_functions!();
}
impl UnaryLayer for UpSampling2D {
fn build(&mut self, input: &Shape) -> Result<(), Error> {
let Some(built) = start_build(&self.built, "UpSampling2D", input)? else {
return Ok(());
};
self.compute_output_shape(&built)?;
self.built = Some(built);
Ok(())
}
fn forward(&self, input: &Tensor, ctx: &mut Ctx) -> Result<Tensor, Error> {
let output = upsample_forward(input, &self.size.0, self.interpolation, 4, "UpSampling2D")?;
if ctx.is_training() {
ctx.push_cache("UpSampling2D", input.shape().to_vec());
}
Ok(output)
}
fn backward(&self, grad_output: &Tensor, ctx: &mut Ctx) -> Result<Tensor, Error> {
let input_shape: Vec<usize> = ctx.pop_cache("UpSampling2D")?;
upsample_backward(
grad_output,
Some(input_shape.as_slice()),
&self.size.0,
self.interpolation,
"UpSampling2D",
)
}
fn compute_output_shape(&self, input: &Shape) -> Result<Shape, Error> {
upsample_output_shape(input, &self.size.0, "UpSampling2D")
}
}