use crate::vae::conv::Conv2d;
use crate::vae::error::{VaeError, VaeResult};
use crate::vae::norm::GroupNorm;
use crate::vae::ops::silu_inplace;
pub struct ResnetBlock2D {
pub norm1: GroupNorm,
pub conv1: Conv2d,
pub norm2: GroupNorm,
pub conv2: Conv2d,
pub conv_shortcut: Option<Conv2d>,
pub in_ch: usize,
pub out_ch: usize,
}
impl ResnetBlock2D {
pub fn forward(&self, input: &[f32], h: usize, w: usize) -> VaeResult<Vec<f32>> {
let hw = h * w;
if input.len() != self.in_ch * hw {
return Err(VaeError::Shape(format!(
"resnet input len {} != in_ch*H*W {}",
input.len(),
self.in_ch * hw
)));
}
let mut hs = input.to_vec();
self.norm1.forward_inplace(&mut hs, h, w)?;
silu_inplace(&mut hs);
let conv1 = self.conv1.forward(&hs, h, w)?;
let mut hs = conv1.data;
self.norm2.forward_inplace(&mut hs, conv1.h, conv1.w)?;
silu_inplace(&mut hs);
let conv2 = self.conv2.forward(&hs, conv1.h, conv1.w)?;
let mut out = conv2.data;
if let Some(shortcut) = self.conv_shortcut.as_ref() {
let res = shortcut.forward(input, h, w)?;
if res.data.len() != out.len() {
return Err(VaeError::Shape(format!(
"resnet shortcut len {} != main len {}",
res.data.len(),
out.len()
)));
}
for (o, r) in out.iter_mut().zip(res.data.iter()) {
*o += *r;
}
} else {
if input.len() != out.len() {
return Err(VaeError::Shape(format!(
"resnet identity residual len {} != main len {} (in_ch {} out_ch {})",
input.len(),
out.len(),
self.in_ch,
self.out_ch
)));
}
for (o, r) in out.iter_mut().zip(input.iter()) {
*o += *r;
}
}
Ok(out)
}
}