use crate::error::{FocrError, FocrResult};
use super::tensor::Mat;
pub fn pixel_shuffle(x: &Mat, s: usize) -> FocrResult<Mat> {
let (seq, d) = (x.rows, x.cols);
if s == 0 {
return Err(FocrError::Other(anyhow::anyhow!(
"pixel_shuffle: scale factor must be non-zero"
)));
}
let g = (seq as f64).sqrt().round() as usize;
if g * g != seq {
return Err(FocrError::Other(anyhow::anyhow!(
"pixel_shuffle: token count {seq} is not a square grid"
)));
}
if g == 0 || !g.is_multiple_of(s) {
return Err(FocrError::Other(anyhow::anyhow!(
"pixel_shuffle: grid side {g} not divisible by scale {s}"
)));
}
let go = g / s; let out_rows = go * go;
let out_cols = d * s * s;
let mut out = vec![0.0f32; out_rows * out_cols];
for r1 in 0..go {
for c1 in 0..go {
let dst_row = &mut out[(r1 * go + c1) * out_cols..(r1 * go + c1 + 1) * out_cols];
for dr in 0..s {
for dc in 0..s {
let src = ((r1 * s + dr) * g + (c1 * s + dc)) * d;
let dst = (dr * s + dc) * d;
dst_row[dst..dst + d].copy_from_slice(&x.data[src..src + d]);
}
}
}
}
Ok(Mat::from_vec(out_rows, out_cols, out))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pixel_shuffle_hand_case() {
let x = Mat::from_vec(16, 1, (0..16).map(|v| v as f32).collect());
let y = pixel_shuffle(&x, 2).unwrap();
assert_eq!((y.rows, y.cols), (4, 4));
assert_eq!(y.row(0), &[0.0, 1.0, 4.0, 5.0]);
assert_eq!(y.row(1), &[2.0, 3.0, 6.0, 7.0]);
assert_eq!(y.row(2), &[8.0, 9.0, 12.0, 13.0]);
assert_eq!(y.row(3), &[10.0, 11.0, 14.0, 15.0]);
}
#[test]
fn pixel_shuffle_scale_one_is_identity() {
let x = Mat::from_vec(9, 3, (0..27).map(|v| v as f32 * 0.5).collect());
let y = pixel_shuffle(&x, 1).unwrap();
assert_eq!(y, x);
}
#[test]
fn pixel_shuffle_keeps_channel_runs() {
let x = Mat::from_vec(4, 3, (0..12).map(|v| v as f32).collect());
let y = pixel_shuffle(&x, 2).unwrap();
assert_eq!((y.rows, y.cols), (1, 12));
assert_eq!(
y.row(0),
&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0]
);
}
#[test]
fn pixel_shuffle_error_handling() {
let x = Mat::from_vec(6, 2, vec![0.0; 12]);
assert!(pixel_shuffle(&x, 2).is_err());
let x = Mat::from_vec(9, 2, vec![0.0; 18]);
assert!(pixel_shuffle(&x, 2).is_err());
let x = Mat::from_vec(4, 2, vec![0.0; 8]);
assert!(pixel_shuffle(&x, 0).is_err());
}
fn load_vision_fixture() -> Option<serde_json::Value> {
let path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/fixtures/smolvlm2/vision_oracle_fixtures.json"
);
let Ok(text) = std::fs::read_to_string(path) else {
eprintln!(
"skip-with-SUCCESS: {path} absent (gen_reference_fixtures_smolvlm2_vision.py)"
);
return None;
};
Some(serde_json::from_str(&text).expect("vision_oracle_fixtures.json parses"))
}
fn mat_from_json(v: &serde_json::Value) -> Mat {
let rows: Vec<Vec<f32>> = v
.as_array()
.unwrap()
.iter()
.map(|row| {
row.as_array()
.unwrap()
.iter()
.map(|x| x.as_f64().unwrap() as f32)
.collect()
})
.collect();
let (r, c) = (rows.len(), rows[0].len());
Mat::from_vec(r, c, rows.into_iter().flatten().collect())
}
#[test]
fn pixel_shuffle_matches_torch_small_case() {
let Some(fx) = load_vision_fixture() else {
return;
};
let small = &fx["l1_pixel_shuffle"]["small"];
assert_eq!(small["s"].as_u64(), Some(4));
let x = mat_from_json(&small["input"]);
let want = mat_from_json(&small["output"]);
let got = pixel_shuffle(&x, 4).unwrap();
assert_eq!(
(got.rows, got.cols),
(want.rows, want.cols),
"shape mismatch"
);
assert_eq!(got.data, want.data, "pixel_shuffle must be BIT-exact");
}
#[test]
fn pixel_shuffle_matches_torch_real_shape() {
use sha2::{Digest, Sha256};
let Some(fx) = load_vision_fixture() else {
return;
};
let real = &fx["l1_pixel_shuffle"]["real_shape"];
let data: Vec<f32> = (0..1024i64 * 768)
.map(|i| ((i % 17) - 8) as f32 * 0.125)
.collect();
let x = Mat::from_vec(1024, 768, data);
let mut h = Sha256::new();
for v in &x.data {
h.update(v.to_le_bytes());
}
assert_eq!(
format!("{:x}", h.finalize()),
real["input_sha256_f32"].as_str().unwrap(),
"rebuilt input drifted from the fixture's input_spec"
);
let y = pixel_shuffle(&x, 4).unwrap();
assert_eq!((y.rows, y.cols), (64, 12288));
let mut h = Sha256::new();
for v in &y.data {
h.update(v.to_le_bytes());
}
assert_eq!(
format!("{:x}", h.finalize()),
real["output"]["sha256_f32"].as_str().unwrap(),
"pixel_shuffle output bytes diverged from the torch reference"
);
}
#[test]
fn smolvlm2_connector_matches_torch_oracle() {
let Ok(dir) = std::env::var("FOCR_SMOLVLM2_DIR") else {
return;
};
let Some(fx) = load_vision_fixture() else {
return;
};
let post_ln_path = format!("{dir}/smolvlm2_vision_post_ln.bin");
let ps_path = format!("{dir}/smolvlm2_pixel_shuffle_out.bin");
let conn_path = format!("{dir}/smolvlm2_connector_out.bin");
let model_path = format!("{dir}/model.safetensors");
if !std::path::Path::new(&post_ln_path).is_file() {
eprintln!("skip-with-SUCCESS: {post_ln_path} absent");
return;
}
let read_f32 = |p: &str| -> Vec<f32> {
let bytes = std::fs::read(p).expect("oracle blob reads");
bytes
.as_chunks::<4>()
.0
.iter()
.map(|c| f32::from_le_bytes(*c))
.collect()
};
let post_ln = read_f32(&post_ln_path);
let n_frames = post_ln.len() / (1024 * 768);
assert_eq!(
n_frames * 1024 * 768,
post_ln.len(),
"post_ln not [F,1024,768]"
);
let ps_want = read_f32(&ps_path);
let conn_want = read_f32(&conn_path);
assert_eq!(ps_want.len(), n_frames * 64 * 12288);
assert_eq!(conn_want.len(), n_frames * 64 * 960);
let mut ps_ours = Vec::with_capacity(ps_want.len());
for f in 0..n_frames {
let x = Mat::from_vec(
1024,
768,
post_ln[f * 1024 * 768..(f + 1) * 1024 * 768].to_vec(),
);
ps_ours.extend_from_slice(&pixel_shuffle(&x, 4).unwrap().data);
}
assert_eq!(
ps_ours, ps_want,
"pixel_shuffle on the real post-LN seam must be BIT-exact"
);
let weights = super::super::weights::Weights::load(std::path::Path::new(&model_path))
.expect("smolvlm2 safetensors loads");
let proj = weights
.mat("model.connector.modality_projection.proj.weight")
.expect("connector proj tensor");
assert_eq!((proj.rows, proj.cols), (960, 12288));
let lin =
super::super::vision_sam::Linear::from_row_major(&proj.data, Vec::new(), 960, 12288)
.expect("connector linear");
let x = Mat::from_vec(n_frames * 64, 12288, ps_ours);
let ours = lin.apply(&x).expect("connector GEMM");
let floor = fx["nondeterminism_floor"]["vision_maxabs_cross_thread"]
.as_f64()
.unwrap();
let tol = (floor * 16.0).max(1.1e-3);
let mut max_abs = 0.0f64;
let mut dot = 0.0f64;
let (mut na, mut nb) = (0.0f64, 0.0f64);
for (a, b) in ours.data.iter().zip(&conn_want) {
let (a, b) = (f64::from(*a), f64::from(*b));
max_abs = max_abs.max((a - b).abs());
dot += a * b;
na += a * a;
nb += b * b;
}
let cos = dot / (na.sqrt() * nb.sqrt());
eprintln!("[C4 parity] connector maxabs={max_abs:.3e} cos={cos:.8} tol={tol:.3e}");
assert!(cos >= 0.9999, "connector cosine {cos} < 0.9999");
assert!(
max_abs <= tol,
"connector maxabs {max_abs:.3e} > tol {tol:.3e} (floor {floor:.3e})"
);
}
}