use crate::math::{dense_matmul, joint_attention};
use crate::vae::error::{VaeError, VaeResult};
use crate::vae::norm::GroupNorm;
pub struct Linear {
pub weight: Vec<f32>,
pub bias: Vec<f32>,
pub out_dim: usize,
pub in_dim: usize,
}
impl Linear {
pub fn from_weights(weight: &[f32], weight_shape: &[usize], bias: &[f32]) -> VaeResult<Self> {
if weight_shape.len() != 2 {
return Err(VaeError::Shape(format!(
"linear weight must be 2-D [out,in], got {weight_shape:?}"
)));
}
let (out_dim, in_dim) = (weight_shape[0], weight_shape[1]);
if weight.len() != out_dim * in_dim {
return Err(VaeError::Shape(format!(
"linear weight len {} != out*in {}",
weight.len(),
out_dim * in_dim
)));
}
if bias.len() != out_dim {
return Err(VaeError::Shape(format!(
"linear bias len {} != out_dim {out_dim}",
bias.len()
)));
}
Ok(Self {
weight: weight.to_vec(),
bias: bias.to_vec(),
out_dim,
in_dim,
})
}
pub fn forward(&self, x: &[f32], rows: usize) -> VaeResult<Vec<f32>> {
let mut out = dense_matmul(x, &self.weight, rows, self.out_dim, self.in_dim)
.map_err(|e| VaeError::Shape(e.to_string()))?;
for r in 0..rows {
let row = &mut out[r * self.out_dim..(r + 1) * self.out_dim];
for (i, v) in row.iter_mut().enumerate() {
*v += self.bias[i];
}
}
Ok(out)
}
}
pub struct AttentionBlock {
pub group_norm: GroupNorm,
pub to_q: Linear,
pub to_k: Linear,
pub to_v: Linear,
pub to_out: Linear,
pub channels: usize,
}
impl AttentionBlock {
pub fn forward(&self, input: &[f32], h: usize, w: usize) -> VaeResult<Vec<f32>> {
let c = self.channels;
let hw = h * w;
if input.len() != c * hw {
return Err(VaeError::Shape(format!(
"attention input len {} != C*H*W {}",
input.len(),
c * hw
)));
}
let mut normed_nchw = input.to_vec();
self.group_norm.forward_inplace(&mut normed_nchw, h, w)?;
let normed = nchw_to_tokens(&normed_nchw, c, hw);
let q = self.to_q.forward(&normed, hw)?;
let k = self.to_k.forward(&normed, hw)?;
let v = self.to_v.forward(&normed, hw)?;
let attended =
joint_attention(&q, &k, &v, 1, hw, c).map_err(|e| VaeError::Shape(e.to_string()))?;
let projected = self.to_out.forward(&attended, hw)?;
let mut out = vec![0.0f32; c * hw];
for ci in 0..c {
for s in 0..hw {
out[ci * hw + s] = input[ci * hw + s] + projected[s * c + ci];
}
}
Ok(out)
}
}
fn nchw_to_tokens(x: &[f32], c: usize, hw: usize) -> Vec<f32> {
let mut out = vec![0.0f32; hw * c];
for ci in 0..c {
for s in 0..hw {
out[s * c + ci] = x[ci * hw + s];
}
}
out
}