pub mod definition;
#[cfg(any(feature = "cpu-reference", feature = "benchmarks"))]
pub mod eval;
mod kernel;
pub mod tune_key;
pub use definition::{InputStage, InterpolateBlueprint, InterpolateStrategy};
use crate::{
definition::{InterpolateError, InterpolateMode, InterpolateOptions},
kernel::{interpolate_launch, interpolate_nearest_backward_launch},
};
use core::result::Result;
use cubecl::{client::Client, prelude::TensorBinding, prelude::*};
pub fn interpolate(
client: &Client,
input: TensorBinding,
output: TensorBinding,
options: InterpolateOptions,
strategy: InterpolateStrategy,
dtype: ElemType,
) -> Result<(), InterpolateError> {
validate_rank(input.shape.len(), output.shape.len())?;
validate_shape(&input.shape)?;
validate_shape(&output.shape)?;
validate_nhwc_consistency(&input.shape, &output.shape)?;
interpolate_launch(client, input, output, options, dtype, strategy)
}
pub fn interpolate_backward(
client: &Client,
input: TensorBinding,
out_grad: TensorBinding,
input_grad: TensorBinding,
options: InterpolateOptions,
dtype: ElemType,
) -> Result<(), InterpolateError> {
validate_rank(input.shape.len(), input_grad.shape.len())?;
validate_rank(out_grad.shape.len(), input_grad.shape.len())?;
validate_shape(&input.shape)?;
validate_shape(&out_grad.shape)?;
validate_shape(&input_grad.shape)?;
validate_nhwc_consistency(&input.shape, &input_grad.shape)?;
validate_nhwc_consistency(&out_grad.shape, &input_grad.shape)?;
if input.shape != input_grad.shape {
return Err(InterpolateError::ShapeMismatch {
input: input.shape.to_vec(),
input_grad: input_grad.shape.to_vec(),
});
}
match options.mode {
InterpolateMode::Nearest(nearest_mode) => {
interpolate_nearest_backward_launch(client, out_grad, input_grad, nearest_mode, dtype)
}
_ => Err(InterpolateError::UnsupportedMode(format!(
"{:?} interpolation backward is not supported by JIT backend",
options.mode
))),
}
}
fn validate_rank(input_rank: usize, output_rank: usize) -> Result<(), InterpolateError> {
if input_rank != 4 || output_rank != 4 {
return Err(InterpolateError::InvalidRank {
input: input_rank,
output: output_rank,
});
}
Ok(())
}
fn validate_shape(shape: &[usize]) -> Result<(), InterpolateError> {
for (axis, &size) in shape.iter().enumerate() {
if size == 0 {
return Err(InterpolateError::ZeroDimension {
shape: shape.to_vec(),
axis,
});
}
if matches!(axis, 1 | 2) && size > i32::MAX as usize {
return Err(InterpolateError::SpatialDimensionTooLarge {
shape: shape.to_vec(),
axis,
size,
max: i32::MAX as usize,
});
}
}
Ok(())
}
fn validate_nhwc_consistency(
input_shape: &[usize],
output_shape: &[usize],
) -> Result<(), InterpolateError> {
if input_shape[0] != output_shape[0] {
return Err(InterpolateError::BatchMismatch {
input: input_shape[0],
output: output_shape[0],
});
}
if input_shape[3] != output_shape[3] {
return Err(InterpolateError::ChannelMismatch {
input: input_shape[3],
output: output_shape[3],
});
}
Ok(())
}