use pagesift_infer::{Activation, LayerNorm, Linear, Tensor, Weights, ops};
use super::attention::Attention;
pub(crate) fn partition(
x: &[f32],
h: usize,
w: usize,
c: usize,
win: usize,
) -> (Vec<f32>, (usize, usize)) {
let (ph, pw) = (h.next_multiple_of(win), w.next_multiple_of(win));
let (nh, nw) = (ph / win, pw / win);
let mut out = vec![0f32; ph * pw * c];
for wy in 0..nh {
for wx in 0..nw {
let base = (wy * nw + wx) * win * win;
for iy in 0..win {
let y = wy * win + iy;
if y >= h {
break;
}
for ix in 0..win {
let xx = wx * win + ix;
if xx >= w {
break;
}
let src = (y * w + xx) * c;
let dst = (base + iy * win + ix) * c;
out[dst..dst + c].copy_from_slice(&x[src..src + c]);
}
}
}
}
(out, (ph, pw))
}
pub(crate) fn unpartition(
win_data: &[f32],
(ph, pw): (usize, usize),
h: usize,
w: usize,
c: usize,
win: usize,
) -> Vec<f32> {
let nw = pw / win;
debug_assert_eq!(win_data.len(), ph * pw * c);
let mut out = vec![0f32; h * w * c];
for y in 0..h {
let (wy, iy) = (y / win, y % win);
for x in 0..w {
let (wx, ix) = (x / win, x % win);
let src = ((wy * nw + wx) * win * win + iy * win + ix) * c;
let dst = (y * w + x) * c;
out[dst..dst + c].copy_from_slice(&win_data[src..src + c]);
}
}
out
}
pub(crate) struct Layer {
ln1: LayerNorm,
attn: Attention,
ln2: LayerNorm,
lin1: Linear,
lin2: Linear,
window: usize,
}
impl Layer {
pub(crate) fn load(
w: &Weights,
p: &str,
heads: usize,
window: usize,
grid: usize,
eps: f32,
) -> Result<Layer, String> {
let side = if window == 0 { grid } else { window };
Ok(Layer {
ln1: LayerNorm::load(w, &format!("{p}.layer_norm1"), eps)?,
attn: Attention::load(w, &format!("{p}.attn"), heads, side)?,
ln2: LayerNorm::load(w, &format!("{p}.layer_norm2"), eps)?,
lin1: Linear::load(w, &format!("{p}.mlp.lin1"))?,
lin2: Linear::load(w, &format!("{p}.mlp.lin2"))?,
window,
})
}
pub(crate) fn forward(&self, x: Tensor, grid: usize) -> Tensor {
let (n, c) = x.tc();
assert_eq!(n, grid * grid, "token 数与网格不符");
let normed = self.ln1.forward(&x);
let attended = if self.window == 0 {
self.attn.forward(&normed)
} else {
let (wins, padded) = partition(&normed.data, grid, grid, c, self.window);
let t = Tensor::new(vec![wins.len() / c, c], wins);
let a = self.attn.forward(&t);
let back = unpartition(&a.data, padded, grid, grid, c, self.window);
Tensor::new(vec![n, c], back)
};
let x = ops::add(&x, &attended);
let mut hdn = self.lin1.forward(&self.ln2.forward(&x));
Activation::Gelu.apply_all(&mut hdn);
ops::add(&x, &self.lin2.forward(&hdn))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn 窗口切分与还原可逆() {
let (h, w, c, win) = (48, 48, 3, 14);
let x: Vec<f32> = (0..h * w * c).map(|v| v as f32).collect();
let (wins, (ph, pw)) = partition(&x, h, w, c, win);
assert_eq!((ph, pw), (56, 56));
assert_eq!(wins.len(), 16 * win * win * c);
assert_eq!(unpartition(&wins, (ph, pw), h, w, c, win), x);
assert!(wins[wins.len() - c..].iter().all(|&v| v == 0.0));
let at = win * win * c;
assert_eq!(wins[at], x[14 * c]);
let at = 4 * win * win * c;
assert_eq!(wins[at], x[14 * w * c]);
}
#[test]
fn 整除时不补边() {
let (h, w, c, win) = (6, 4, 2, 2);
let x: Vec<f32> = (0..h * w * c).map(|v| v as f32 + 1.0).collect();
let (wins, p) = partition(&x, h, w, c, win);
assert_eq!(p, (6, 4));
assert!(wins.iter().all(|&v| v != 0.0));
assert_eq!(unpartition(&wins, p, h, w, c, win), x);
}
}