use crate::error::Error;
use crate::neural_network::layers::convolution::convolution_engine::{
ConvPadding, effective_kernel,
};
pub(super) fn validate_filters(filters: usize) -> Result<(), Error> {
if filters == 0 {
return Err(Error::invalid_parameter(
"filters",
"Number of filters must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_kernel_size_1d(kernel_size: usize) -> Result<(), Error> {
if kernel_size == 0 {
return Err(Error::invalid_parameter(
"kernel_size",
"Kernel size must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_kernel_size_2d(kernel_size: (usize, usize)) -> Result<(), Error> {
if kernel_size.0 == 0 || kernel_size.1 == 0 {
return Err(Error::invalid_parameter(
"kernel_size",
"Kernel dimensions must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_kernel_size_3d(kernel_size: (usize, usize, usize)) -> Result<(), Error> {
if kernel_size.0 == 0 || kernel_size.1 == 0 || kernel_size.2 == 0 {
return Err(Error::invalid_parameter(
"kernel_size",
"Kernel dimensions must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_strides_1d(stride: usize) -> Result<(), Error> {
if stride == 0 {
return Err(Error::invalid_parameter(
"stride",
"Stride must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_strides_2d(strides: (usize, usize)) -> Result<(), Error> {
if strides.0 == 0 || strides.1 == 0 {
return Err(Error::invalid_parameter(
"strides",
"Strides must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_strides_3d(strides: (usize, usize, usize)) -> Result<(), Error> {
if strides.0 == 0 || strides.1 == 0 || strides.2 == 0 {
return Err(Error::invalid_parameter(
"strides",
"Strides must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_input_shape_1d(input_shape: &[usize]) -> Result<(), Error> {
if input_shape.len() != 3 {
return Err(Error::invalid_input(
"Input shape must be 3D: [batch_size, length, channels]",
));
}
if input_shape[2] == 0 {
return Err(Error::invalid_input(
"Number of input channels must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_input_shape_2d(input_shape: &[usize]) -> Result<(), Error> {
if input_shape.len() != 4 {
return Err(Error::invalid_input(
"Input shape must be 4D: [batch_size, height, width, channels]",
));
}
if input_shape[3] == 0 {
return Err(Error::invalid_input(
"Number of input channels must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_input_shape_3d(input_shape: &[usize]) -> Result<(), Error> {
if input_shape.len() != 5 {
return Err(Error::invalid_input(
"Input shape must be 5-dimensional: [batch, depth, height, width, channels]",
));
}
if input_shape.contains(&0) {
return Err(Error::invalid_input(
"All input dimensions must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_transpose_input_shape(
input_shape: &[usize],
rank: usize,
layout: &str,
) -> Result<(), Error> {
if input_shape.len() != rank + 2 {
return Err(Error::invalid_input(format!(
"Input shape must be {}D: {layout}",
rank + 2
)));
}
if input_shape.contains(&0) {
return Err(Error::invalid_input(
"All input dimensions must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_dilation(dilation: &[usize]) -> Result<(), Error> {
if dilation.contains(&0) {
return Err(Error::invalid_parameter(
"dilation_rate",
"Dilation rate must be greater than 0",
));
}
Ok(())
}
pub(super) fn validate_valid_kernel_fits(
padding: ConvPadding,
kernel: &[usize],
dilation: &[usize],
input_sp: &[usize],
) -> Result<(), Error> {
if padding != ConvPadding::Valid {
return Ok(());
}
for d in 0..kernel.len() {
let keff = effective_kernel(kernel[d], dilation[d]);
if input_sp[d] < keff {
return Err(Error::invalid_input(format!(
"Valid-padding convolution requires every input spatial dimension to be at least \
the dilated kernel size: axis {d} has input size {} < dilated kernel size {keff}",
input_sp[d]
)));
}
}
Ok(())
}
pub(super) fn valid_output_size(
layer: &str,
axis: &str,
input: usize,
keff: usize,
stride: usize,
) -> Result<usize, Error> {
let Some(rest) = input.checked_sub(keff) else {
return Err(Error::invalid_input(format!(
"{layer} under Valid padding needs an input {axis} of at least the effective kernel \
extent, which is {keff}. This input {axis} is {input}. Use Same padding, a smaller \
kernel, a smaller dilation rate, or a larger input"
)));
};
Ok(rest / stride + 1)
}
pub(super) fn validate_stride_dilation_exclusive(
strides: &[usize],
dilation: &[usize],
) -> Result<(), Error> {
let max_stride = strides.iter().copied().max().unwrap_or(1);
let max_dilation = dilation.iter().copied().max().unwrap_or(1);
if max_stride > 1 && max_dilation > 1 {
return Err(Error::invalid_parameter(
"dilation_rate",
format!(
"A stride above 1 does not combine with a dilation above 1: strides {strides:?} \
and dilation {dilation:?}"
),
));
}
Ok(())
}
pub(super) fn validate_depth_multiplier(depth_multiplier: usize) -> Result<(), Error> {
if depth_multiplier == 0 {
return Err(Error::invalid_parameter(
"depth_multiplier",
"Depth multiplier must be greater than 0",
));
}
Ok(())
}