use crate::Engine;
use cudarc::driver::CudaSlice;
use memra_gguf::dequant::bf16_to_f32;
use memra_gguf::safetensors::StModel;
use std::path::Path;
pub const SV_HIDDEN: usize = 1536;
pub const SV_HEADS: usize = 16;
pub const SV_HEAD_DIM: usize = SV_HIDDEN / SV_HEADS; pub const SV_INTER: usize = 8960; pub const SV_DEPTH: usize = 47;
pub const SV_PATCH: usize = 14;
pub const SV_POS_GRID: usize = 52; pub const SV_PATCH_IN: usize = 3 * SV_PATCH * SV_PATCH; pub const SV_IMAGE_SIZE: usize = 728;
pub const SV_TILE_SIZE: usize = 504;
pub const SV_GRID_MAIN: usize = SV_IMAGE_SIZE / SV_PATCH; pub const SV_GRID_TILE: usize = SV_TILE_SIZE / SV_PATCH; pub const SV_MAIN_ROWS: usize = 169;
pub const SV_TILE_ROWS: usize = 81;
pub const SV_MAX_IMAGE_SIZE: usize = 3024;
const LN_EPS: f32 = 1e-5;
const ROPE_THETA: f32 = 10000.0;
const MEAN: [f32; 3] = [0.481_454_66, 0.457_827_5, 0.408_210_73];
const STD: [f32; 3] = [0.268_629_54, 0.261_302_6, 0.275_777_1];
struct Lin {
w: CudaSlice<f32>,
b: Option<CudaSlice<f32>>,
in_f: usize,
out_f: usize,
}
struct SBlock {
ln1_w: CudaSlice<f32>,
ln1_b: CudaSlice<f32>,
ln2_w: CudaSlice<f32>,
ln2_b: CudaSlice<f32>,
ls1: Vec<f32>,
ls2: Vec<f32>,
qkv: Lin,
proj: Lin,
fc: Lin,
cproj: Lin,
}
struct Conv3x3s2 {
w: CudaSlice<f32>, b: CudaSlice<f32>,
c_in: usize,
c_out: usize,
}
pub struct StepVisionTower {
patch: Lin, pos: Vec<f32>,
ln_pre_w: CudaSlice<f32>,
ln_pre_b: CudaSlice<f32>,
blocks: Vec<SBlock>,
down1: Conv3x3s2, down2: Conv3x3s2, proj: Lin, }
fn read_f32(m: &StModel, name: &str) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
let (info, raw) = m
.raw(name)
.ok_or_else(|| format!("step vision tensor missing: {name}"))?;
match info.dtype.as_str() {
"BF16" => Ok(raw
.chunks_exact(2)
.map(|c| bf16_to_f32(u16::from_le_bytes([c[0], c[1]])))
.collect()),
"F32" => Ok(raw
.chunks_exact(4)
.map(|c| f32::from_le_bytes(c.try_into().unwrap()))
.collect()),
other => Err(format!("step vision tensor {name}: unsupported dtype {other}").into()),
}
}
fn load_lin(
e: &Engine,
m: &StModel,
stem: &str,
in_f: usize,
out_f: usize,
bias: bool,
) -> Result<Lin, Box<dyn std::error::Error>> {
let w = read_f32(m, &format!("{stem}.weight"))?;
assert_eq!(w.len(), in_f * out_f, "{stem}.weight shape");
let b = if bias {
let b = read_f32(m, &format!("{stem}.bias"))?;
assert_eq!(b.len(), out_f, "{stem}.bias shape");
Some(e.htod(&b)?)
} else {
None
};
Ok(Lin {
w: e.htod(&w)?,
b,
in_f,
out_f,
})
}
fn window_size(long: usize, short: usize) -> usize {
if long <= SV_IMAGE_SIZE {
if long as f64 / short as f64 > 1.5 {
short
} else {
0
}
} else if long as f64 / short as f64 > 4.0 {
short.min(SV_TILE_SIZE)
} else {
SV_TILE_SIZE
}
}
fn pad_rule(w: usize, h: usize) -> (usize, usize) {
let ratio = w as f64 / h as f64;
if w.min(h) < 32 && !(0.25..=4.0).contains(&ratio) {
let s = w.max(h);
(s, s)
} else {
(w, h)
}
}
fn cap_rule(w: usize, h: usize) -> (usize, usize) {
if w.max(h) > SV_MAX_IMAGE_SIZE {
let s = SV_MAX_IMAGE_SIZE as f64 / w.max(h) as f64;
((w as f64 * s) as usize, (h as f64 * s) as usize)
} else {
(w, h)
}
}
fn crop_snap(side: usize, win: usize) -> usize {
let ratio = side as f64 / win as f64;
if ratio < 1.0 {
return side;
}
let whole = side / win;
let n = if ratio - whole as f64 > 0.2 {
whole + 1
} else {
whole
};
win * n
}
pub struct StepImagePlan {
pub n_tiles: usize,
pub newline_mask: Vec<bool>,
}
impl StepImagePlan {
pub fn n_rows(&self) -> usize {
self.n_tiles * SV_TILE_ROWS + SV_MAIN_ROWS
}
pub fn n_prompt_tokens(&self) -> usize {
let newlines = self.newline_mask.iter().filter(|&&b| b).count();
self.n_tiles * (SV_TILE_ROWS + 2) + newlines + SV_MAIN_ROWS + 2
}
}
fn plan_for_dims(w0: usize, h0: usize) -> StepImagePlan {
let (w, h) = pad_rule(w0, h0);
let (w, h) = cap_rule(w, h);
let win = window_size(w.max(h), w.min(h));
if win == 0 {
return StepImagePlan {
n_tiles: 0,
newline_mask: Vec::new(),
};
}
let (cw, ch) = (crop_snap(w, win), crop_snap(h, win));
let x_num = (cw / win).max(1);
let y_num = (ch / win).max(1);
let n = x_num * y_num;
let mut mask = vec![false; n];
let mut newlines: Vec<usize> = (0..n).filter(|i| (i + 1) % x_num == 0).collect();
if newlines.last() == Some(&(n - 1)) {
newlines.pop(); }
for i in newlines {
mask[i] = true;
}
StepImagePlan {
n_tiles: n,
newline_mask: mask,
}
}
pub fn step_plan_image(bytes: &[u8]) -> Result<StepImagePlan, String> {
let (w, h) = crate::vision_pre::image_header_dims(bytes)?;
if w.saturating_mul(h) > crate::vision_pre::IMG_MAX_DECODE_PIXELS {
return Err(format!(
"image {w}x{h} exceeds the decode budget ({} px) — refused before decode",
crate::vision_pre::IMG_MAX_DECODE_PIXELS
));
}
if w < 2 || h < 2 {
return Err(format!("image too small: {w}x{h}"));
}
Ok(plan_for_dims(w, h))
}
pub struct StepVisionUnit {
pub main: Vec<f32>,
pub tiles: Vec<Vec<f32>>,
pub newline_mask: Vec<bool>,
}
impl StepVisionUnit {
pub fn n_rows(&self) -> usize {
self.tiles.len() * SV_TILE_ROWS + SV_MAIN_ROWS
}
}
fn patchify(img: &image::RgbImage, g: usize) -> Vec<f32> {
let mut rows = vec![0f32; g * g * SV_PATCH_IN];
for py in 0..g {
for px in 0..g {
let dst = &mut rows[(py * g + px) * SV_PATCH_IN..(py * g + px + 1) * SV_PATCH_IN];
for c in 0..3 {
for ky in 0..SV_PATCH {
for kx in 0..SV_PATCH {
let p =
img.get_pixel((px * SV_PATCH + kx) as u32, (py * SV_PATCH + ky) as u32);
dst[(c * SV_PATCH + ky) * SV_PATCH + kx] =
((p[c] as f32) / 255.0 - MEAN[c]) / STD[c];
}
}
}
}
}
rows
}
pub fn step_prep_image(bytes: &[u8]) -> Result<StepVisionUnit, Box<dyn std::error::Error>> {
step_plan_image(bytes)?;
let (hw, hh) = crate::vision_pre::image_header_dims(bytes)?;
let mut reader = image::ImageReader::new(std::io::Cursor::new(bytes)).with_guessed_format()?;
let mut limits = image::Limits::default();
limits.max_image_width = Some(hw as u32);
limits.max_image_height = Some(hh as u32);
reader.limits(limits);
let mut img = reader.decode()?.to_rgb8();
let (w0, h0) = (img.width() as usize, img.height() as usize);
let (pw, ph) = pad_rule(w0, h0);
if (pw, ph) != (w0, h0) {
let mut padded = image::RgbImage::new(pw as u32, ph as u32);
image::imageops::replace(&mut padded, &img, 0, 0);
img = padded;
}
let (cw, ch) = cap_rule(img.width() as usize, img.height() as usize);
if (cw, ch) != (img.width() as usize, img.height() as usize) {
img = image::imageops::resize(
&img,
cw as u32,
ch as u32,
image::imageops::FilterType::Triangle,
);
}
let (w, h) = (img.width() as usize, img.height() as usize);
let main_img = image::imageops::resize(
&img,
SV_IMAGE_SIZE as u32,
SV_IMAGE_SIZE as u32,
image::imageops::FilterType::Triangle,
);
let main = patchify(&main_img, SV_GRID_MAIN);
let win = window_size(w.max(h), w.min(h));
let (mut tiles, mut newline_mask) = (Vec::new(), Vec::new());
if win > 0 {
let (sw, sh) = (crop_snap(w, win), crop_snap(h, win));
let snapped = if (sw, sh) != (w, h) {
image::imageops::resize(
&img,
sw as u32,
sh as u32,
image::imageops::FilterType::Triangle,
)
} else {
img
};
let x_num = (sw / win).max(1);
let y_num = (sh / win).max(1);
let n = x_num * y_num;
for ty in 0..y_num {
for tx in 0..x_num {
let crop = image::imageops::crop_imm(
&snapped,
(tx * win) as u32,
(ty * win) as u32,
win as u32,
win as u32,
)
.to_image();
let tile = image::imageops::resize(
&crop,
SV_TILE_SIZE as u32,
SV_TILE_SIZE as u32,
image::imageops::FilterType::Triangle,
);
tiles.push(patchify(&tile, SV_GRID_TILE));
}
}
let mut newlines: Vec<usize> = (0..n).filter(|i| (i + 1) % x_num == 0).collect();
if newlines.last() == Some(&(n - 1)) {
newlines.pop();
}
newline_mask = vec![false; n];
for i in newlines {
newline_mask[i] = true;
}
}
Ok(StepVisionUnit {
main,
tiles,
newline_mask,
})
}
impl StepVisionTower {
pub fn load(e: &Engine, dir: &Path) -> Result<Self, Box<dyn std::error::Error>> {
let m = StModel::open(dir)?;
let p = "model.vision_model";
let patch = {
let w = read_f32(&m, &format!("{p}.conv1.weight"))?;
assert_eq!(w.len(), SV_HIDDEN * SV_PATCH_IN, "conv1.weight shape");
Lin {
w: e.htod(&w)?,
b: None,
in_f: SV_PATCH_IN,
out_f: SV_HIDDEN,
}
};
let pos = read_f32(&m, &format!("{p}.positional_embedding"))?;
assert_eq!(
pos.len(),
SV_POS_GRID * SV_POS_GRID * SV_HIDDEN,
"positional_embedding shape"
);
let ln_pre_w = e.htod(&read_f32(&m, &format!("{p}.ln_pre.weight"))?)?;
let ln_pre_b = e.htod(&read_f32(&m, &format!("{p}.ln_pre.bias"))?)?;
let mut blocks = Vec::with_capacity(SV_DEPTH);
for il in 0..SV_DEPTH {
let bp = format!("{p}.transformer.resblocks.{il}");
let ls1 = read_f32(&m, &format!("{bp}.ls_1.gamma"))?;
let ls2 = read_f32(&m, &format!("{bp}.ls_2.gamma"))?;
assert_eq!(ls1.len(), SV_HIDDEN, "ls_1.gamma shape");
assert_eq!(ls2.len(), SV_HIDDEN, "ls_2.gamma shape");
blocks.push(SBlock {
ln1_w: e.htod(&read_f32(&m, &format!("{bp}.ln_1.weight"))?)?,
ln1_b: e.htod(&read_f32(&m, &format!("{bp}.ln_1.bias"))?)?,
ln2_w: e.htod(&read_f32(&m, &format!("{bp}.ln_2.weight"))?)?,
ln2_b: e.htod(&read_f32(&m, &format!("{bp}.ln_2.bias"))?)?,
ls1,
ls2,
qkv: {
let w = read_f32(&m, &format!("{bp}.attn.in_proj_weight"))?;
let b = read_f32(&m, &format!("{bp}.attn.in_proj_bias"))?;
assert_eq!(w.len(), 3 * SV_HIDDEN * SV_HIDDEN, "in_proj_weight shape");
assert_eq!(b.len(), 3 * SV_HIDDEN, "in_proj_bias shape");
Lin {
w: e.htod(&w)?,
b: Some(e.htod(&b)?),
in_f: SV_HIDDEN,
out_f: 3 * SV_HIDDEN,
}
},
proj: load_lin(
e,
&m,
&format!("{bp}.attn.out_proj"),
SV_HIDDEN,
SV_HIDDEN,
true,
)?,
fc: load_lin(e, &m, &format!("{bp}.mlp.c_fc"), SV_HIDDEN, SV_INTER, true)?,
cproj: load_lin(
e,
&m,
&format!("{bp}.mlp.c_proj"),
SV_INTER,
SV_HIDDEN,
true,
)?,
});
}
let load_conv = |stem: &str,
c_in: usize,
c_out: usize|
-> Result<Conv3x3s2, Box<dyn std::error::Error>> {
let w = read_f32(&m, &format!("{stem}.weight"))?;
let b = read_f32(&m, &format!("{stem}.bias"))?;
assert_eq!(w.len(), c_out * c_in * 9, "{stem}.weight shape");
assert_eq!(b.len(), c_out, "{stem}.bias shape");
Ok(Conv3x3s2 {
w: e.htod(&w)?,
b: e.htod(&b)?,
c_in,
c_out,
})
};
let down1 = load_conv(&format!("{p}.vit_downsampler1"), SV_HIDDEN, 2 * SV_HIDDEN)?;
let down2 = load_conv(
&format!("{p}.vit_downsampler2"),
2 * SV_HIDDEN,
4 * SV_HIDDEN,
)?;
let proj = {
let w = read_f32(&m, "model.vit_large_projector.weight")?;
assert_eq!(w.len() % (4 * SV_HIDDEN), 0, "vit_large_projector shape");
let out_f = w.len() / (4 * SV_HIDDEN);
Lin {
w: e.htod(&w)?,
b: None,
in_f: 4 * SV_HIDDEN,
out_f,
}
};
eprintln!(
"[step-vision] tower loaded from {} ({SV_DEPTH} blocks, out_width {}, f32-resident)",
dir.display(),
proj.out_f
);
Ok(Self {
patch,
pos,
ln_pre_w,
ln_pre_b,
blocks,
down1,
down2,
proj,
})
}
pub fn out_width(&self) -> usize {
self.proj.out_f
}
fn linear_bias(
&self,
e: &Engine,
x: &CudaSlice<f32>,
l: &Lin,
m: usize,
) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
let mut y = e.linear(x, &l.w, m, l.in_f, l.out_f)?;
if let Some(b) = &l.b {
for r in 0..m {
e.add_row_inplace(&mut y, b, l.out_f, r * l.out_f)?;
}
}
Ok(y)
}
fn pos_for_grid(&self, g: usize) -> Vec<f32> {
if g == SV_POS_GRID {
return self.pos.clone();
}
let scale = SV_POS_GRID as f32 / g as f32;
let mut out = vec![0f32; g * g * SV_HIDDEN];
for y in 0..g {
for x in 0..g {
let sy = ((y as f32 + 0.5) * scale - 0.5).clamp(0.0, (SV_POS_GRID - 1) as f32);
let sx = ((x as f32 + 0.5) * scale - 0.5).clamp(0.0, (SV_POS_GRID - 1) as f32);
let (y0, x0) = (sy.floor() as usize, sx.floor() as usize);
let (y1, x1) = ((y0 + 1).min(SV_POS_GRID - 1), (x0 + 1).min(SV_POS_GRID - 1));
let (fy, fx) = (sy - y0 as f32, sx - x0 as f32);
let dst = &mut out[(y * g + x) * SV_HIDDEN..(y * g + x + 1) * SV_HIDDEN];
#[allow(clippy::needless_range_loop)]
for c in 0..SV_HIDDEN {
let p00 = self.pos[(y0 * SV_POS_GRID + x0) * SV_HIDDEN + c];
let p01 = self.pos[(y0 * SV_POS_GRID + x1) * SV_HIDDEN + c];
let p10 = self.pos[(y1 * SV_POS_GRID + x0) * SV_HIDDEN + c];
let p11 = self.pos[(y1 * SV_POS_GRID + x1) * SV_HIDDEN + c];
dst[c] = p00 * (1.0 - fy) * (1.0 - fx)
+ p01 * (1.0 - fy) * fx
+ p10 * fy * (1.0 - fx)
+ p11 * fy * fx;
}
}
}
out
}
fn im2col(x: &[f32], g: usize, c_in: usize) -> (Vec<f32>, usize) {
let og = (g - 1) / 2 + 1;
let mut out = vec![0f32; og * og * c_in * 9];
for oy in 0..og {
for ox in 0..og {
let dst = &mut out[(oy * og + ox) * c_in * 9..(oy * og + ox + 1) * c_in * 9];
for ky in 0..3usize {
for kx in 0..3usize {
let iy = (2 * oy + ky) as isize - 1;
let ix = (2 * ox + kx) as isize - 1;
if iy < 0 || ix < 0 || iy >= g as isize || ix >= g as isize {
continue; }
let src = &x[((iy as usize) * g + ix as usize) * c_in..];
for c in 0..c_in {
dst[c * 9 + ky * 3 + kx] = src[c];
}
}
}
}
}
(out, og)
}
pub fn forward(
&self,
e: &Engine,
patches: &[f32],
g: usize,
) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
let n = g * g;
assert_eq!(patches.len(), n * SV_PATCH_IN, "patch buffer shape");
if n > 12288 {
return Err(format!(
"step vision segment {n} patches exceeds the sdpa shared-memory ceiling (12288)"
)
.into());
}
let dbg = std::env::var("MEMRA_VISION_DEBUG").ok();
let dump = |tag: &str, buf: &[f32]| {
if let Some(dir) = dbg.as_deref() {
let raw: Vec<u8> = buf.iter().flat_map(|v| v.to_le_bytes()).collect();
let _ = std::fs::write(format!("{dir}/rust_{tag}.bin"), raw);
}
};
let xd = e.htod(patches)?;
let embedded = self.linear_bias(e, &xd, &self.patch, n)?;
let pos = self.pos_for_grid(g);
let pos_d = e.htod(&pos)?;
let mut summed = e.zeros(n * SV_HIDDEN)?;
e.add(&embedded, &pos_d, &mut summed, n * SV_HIDDEN)?;
let mut x = e.zeros(n * SV_HIDDEN)?;
e.layer_norm_bias(
&summed,
&self.ln_pre_w,
&self.ln_pre_b,
&mut x,
SV_HIDDEN,
n,
LN_EPS,
)?;
if dbg.is_some() {
dump("pre_blocks", &e.dtoh(&x)?);
}
let half = SV_HEAD_DIM / 2; let quarter = half / 2; let inv_freq: Vec<f32> = (0..quarter)
.map(|i| ROPE_THETA.powf(-2.0 * (i as f32) / half as f32))
.collect();
let mut cos_t = vec![0f32; n * half]; let mut sin_t = vec![0f32; n * half];
for t in 0..n {
let (row, col) = (t / g, t % g);
for i in 0..quarter {
let ac = col as f32 * inv_freq[i];
let ar = row as f32 * inv_freq[i];
cos_t[t * half + i] = ac.cos();
sin_t[t * half + i] = ac.sin();
cos_t[t * half + quarter + i] = ar.cos();
sin_t[t * half + quarter + i] = ar.sin();
}
}
let scale = 1.0 / (SV_HEAD_DIM as f32).sqrt();
for (ib, blk) in self.blocks.iter().enumerate() {
let mut h = e.zeros(n * SV_HIDDEN)?;
e.layer_norm_bias(&x, &blk.ln1_w, &blk.ln1_b, &mut h, SV_HIDDEN, n, LN_EPS)?;
let qkv = self.linear_bias(e, &h, &blk.qkv, n)?;
let qkv_h = e.dtoh(&qkv)?;
let mut qh = vec![0f32; n * SV_HIDDEN];
let mut kh = vec![0f32; n * SV_HIDDEN];
let mut vh = vec![0f32; n * SV_HIDDEN];
for t in 0..n {
let row = &qkv_h[t * 3 * SV_HIDDEN..(t + 1) * 3 * SV_HIDDEN];
let dst = t * SV_HIDDEN;
vh[dst..dst + SV_HIDDEN].copy_from_slice(&row[2 * SV_HIDDEN..3 * SV_HIDDEN]);
for hd in 0..SV_HEADS {
let o = hd * SV_HEAD_DIM;
for hf in 0..2usize {
for i in 0..quarter {
let (c, s) = (
cos_t[t * half + hf * quarter + i],
sin_t[t * half + hf * quarter + i],
);
let d = hf * half + 2 * i;
let (qa, qb) = (row[o + d], row[o + d + 1]);
qh[dst + o + d] = qa * c - qb * s;
qh[dst + o + d + 1] = qb * c + qa * s;
let (ka, kb) = (row[SV_HIDDEN + o + d], row[SV_HIDDEN + o + d + 1]);
kh[dst + o + d] = ka * c - kb * s;
kh[dst + o + d + 1] = kb * c + ka * s;
}
}
}
}
let (qd, kd, vd) = (e.htod(&qh)?, e.htod(&kh)?, e.htod(&vh)?);
let mut od = e.zeros(n * SV_HIDDEN)?;
e.sdpa_naive(
&qd,
&kd,
&vd,
&mut od,
SV_HEAD_DIM,
SV_HEADS,
SV_HEADS,
n,
n,
scale,
false,
)?;
let attn = self.linear_bias(e, &od, &blk.proj, n)?;
let mut ah = e.dtoh(&attn)?;
for t in 0..n {
for c in 0..SV_HIDDEN {
ah[t * SV_HIDDEN + c] *= blk.ls1[c];
}
}
let ad = e.htod(&ah)?;
let mut xr = e.zeros(n * SV_HIDDEN)?;
e.add(&x, &ad, &mut xr, n * SV_HIDDEN)?;
let mut h2 = e.zeros(n * SV_HIDDEN)?;
e.layer_norm_bias(&xr, &blk.ln2_w, &blk.ln2_b, &mut h2, SV_HIDDEN, n, LN_EPS)?;
let f1 = self.linear_bias(e, &h2, &blk.fc, n)?;
let mut fh = e.dtoh(&f1)?;
for v in fh.iter_mut() {
*v = *v / (1.0 + (-1.702 * *v).exp());
}
let fd = e.htod(&fh)?;
let f2 = self.linear_bias(e, &fd, &blk.cproj, n)?;
let mut mh = e.dtoh(&f2)?;
for t in 0..n {
for c in 0..SV_HIDDEN {
mh[t * SV_HIDDEN + c] *= blk.ls2[c];
}
}
let md = e.htod(&mh)?;
let mut xn = e.zeros(n * SV_HIDDEN)?;
e.add(&xr, &md, &mut xn, n * SV_HIDDEN)?;
x = xn;
if dbg.is_some() && ib == 0 {
dump("blk0", &e.dtoh(&x)?);
}
}
if dbg.is_some() {
dump("post_blocks", &e.dtoh(&x)?);
}
let xh = e.dtoh(&x)?;
let (col1, g1) = Self::im2col(&xh, g, SV_HIDDEN);
let c1 = e.htod(&col1)?;
let mut y1 = e.linear(
&c1,
&self.down1.w,
g1 * g1,
self.down1.c_in * 9,
self.down1.c_out,
)?;
for r in 0..g1 * g1 {
e.add_row_inplace(
&mut y1,
&self.down1.b,
self.down1.c_out,
r * self.down1.c_out,
)?;
}
let y1h = e.dtoh(&y1)?;
let (col2, g2) = Self::im2col(&y1h, g1, self.down2.c_in);
let c2 = e.htod(&col2)?;
let mut y2 = e.linear(
&c2,
&self.down2.w,
g2 * g2,
self.down2.c_in * 9,
self.down2.c_out,
)?;
for r in 0..g2 * g2 {
e.add_row_inplace(
&mut y2,
&self.down2.b,
self.down2.c_out,
r * self.down2.c_out,
)?;
}
if dbg.is_some() {
dump("downsampled", &e.dtoh(&y2)?);
}
let out = self.linear_bias(e, &y2, &self.proj, g2 * g2)?;
if dbg.is_some() {
dump("projected", &e.dtoh(&out)?);
}
Ok(out)
}
pub fn forward_unit(
&self,
e: &Engine,
unit: &StepVisionUnit,
rows: &mut CudaSlice<f32>,
row_off: usize,
) -> Result<usize, Box<dyn std::error::Error>> {
let w = self.out_width();
let mut off = row_off;
for tile in &unit.tiles {
let emb = self.forward(e, tile, SV_GRID_TILE)?;
e.dtod_copy_into(&emb, rows, off * w)?;
off += SV_TILE_ROWS;
}
let emb = self.forward(e, &unit.main, SV_GRID_MAIN)?;
e.dtod_copy_into(&emb, rows, off * w)?;
off += SV_MAIN_ROWS;
Ok(off - row_off)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tiling_plan_cells() {
let p = plan_for_dims(600, 400);
assert_eq!((p.n_tiles, p.n_prompt_tokens()), (0, 171));
let p = plan_for_dims(728, 728);
assert_eq!(p.n_tiles, 0);
let p = plan_for_dims(700, 300);
assert_eq!(p.n_tiles, 3);
assert_eq!(p.newline_mask, vec![false, false, false]);
let p = plan_for_dims(1600, 900);
assert_eq!(p.n_tiles, 6);
assert_eq!(
p.newline_mask,
vec![false, false, true, false, false, false]
);
assert_eq!(p.n_prompt_tokens(), 670);
let p = plan_for_dims(200, 20);
assert_eq!(p.n_tiles, 0);
let p = plan_for_dims(4000, 1000);
assert_eq!(p.n_tiles, 12);
assert_eq!(p.n_rows(), 12 * 81 + 169);
}
#[test]
fn downsampler_geometry() {
let x = vec![0f32; 52 * 52 * 4];
let (_, og) = StepVisionTower::im2col(&x, 52, 4);
assert_eq!(og, 26);
let x = vec![0f32; 26 * 26 * 4];
let (_, og) = StepVisionTower::im2col(&x, 26, 4);
assert_eq!(og, 13);
let x = vec![0f32; 36 * 36 * 4];
let (_, og) = StepVisionTower::im2col(&x, 36, 4);
assert_eq!(og, 18);
let x = vec![0f32; 18 * 18 * 4];
let (_, og) = StepVisionTower::im2col(&x, 18, 4);
assert_eq!(og, 9);
}
#[test]
fn im2col_values() {
let x: Vec<f32> = (1..=9).map(|v| v as f32).collect();
let (col, og) = StepVisionTower::im2col(&x, 3, 1);
assert_eq!(og, 2);
assert_eq!(&col[0..9], &[0., 0., 0., 0., 1., 2., 0., 4., 5.]);
assert_eq!(&col[27..36], &[5., 6., 0., 8., 9., 0., 0., 0., 0.]);
}
fn png_bytes(w: u32, h: u32) -> Vec<u8> {
let img = image::RgbImage::from_fn(w, h, |x, y| {
image::Rgb([(x % 251) as u8, (y % 241) as u8, ((x + y) % 253) as u8])
});
let mut buf = std::io::Cursor::new(Vec::new());
img.write_to(&mut buf, image::ImageFormat::Png).unwrap();
buf.into_inner()
}
#[test]
fn prep_matches_plan() {
for (w, h) in [(64u32, 64u32), (1600, 900), (700, 300), (900, 3000)] {
let bytes = png_bytes(w, h);
let plan = step_plan_image(&bytes).unwrap();
let prep = step_prep_image(&bytes).unwrap();
assert_eq!(prep.tiles.len(), plan.n_tiles, "{w}x{h} tile count");
assert_eq!(prep.newline_mask, plan.newline_mask, "{w}x{h} newline mask");
assert_eq!(prep.main.len(), SV_GRID_MAIN * SV_GRID_MAIN * SV_PATCH_IN);
for t in &prep.tiles {
assert_eq!(t.len(), SV_GRID_TILE * SV_GRID_TILE * SV_PATCH_IN);
}
assert_eq!(prep.n_rows(), plan.n_rows());
}
}
#[test]
fn patchify_normalization() {
let img = image::RgbImage::from_pixel(
SV_IMAGE_SIZE as u32,
SV_IMAGE_SIZE as u32,
image::Rgb([128, 128, 128]),
);
let rows = patchify(&img, SV_GRID_MAIN);
let want: Vec<f32> = (0..3).map(|c| (128.0 / 255.0 - MEAN[c]) / STD[c]).collect();
let r0 = &rows[..SV_PATCH_IN];
for c in 0..3 {
for i in 0..SV_PATCH * SV_PATCH {
assert!((r0[c * SV_PATCH * SV_PATCH + i] - want[c]).abs() < 1e-6);
}
}
}
}