use model_io::{VisionConfig, MAX_VISION_DEEPSTACK_MERGERS};
use crate::real_forward_types::RealForwardError;
pub(crate) const VISION_LAYER_NORM_EPS: f32 = 1e-6;
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct VisionShape {
pub(crate) depth: usize,
pub(crate) hidden: usize,
pub(crate) intermediate: usize,
pub(crate) heads: usize,
pub(crate) head_dim: usize,
pub(crate) merge: usize,
pub(crate) out_hidden: usize,
pub(crate) patch_dim: usize,
pub(crate) pos_rows: usize,
pub(crate) pos_side: usize,
pub(crate) patch_size: usize,
pub(crate) deepstack: Vec<usize>,
}
impl VisionShape {
pub(crate) fn merger_input(&self) -> usize {
self.hidden * self.merge * self.merge
}
pub(crate) fn patches_per_token(&self) -> usize {
self.merge * self.merge
}
pub(crate) fn attention_scale(&self) -> f32 {
1.0 / (self.head_dim as f32).sqrt()
}
pub(crate) fn resolve(vision: &VisionConfig) -> Result<Self, RealForwardError> {
let unsupported = |detail: String| RealForwardError::Unsupported(detail);
if !vision.is_active() {
return Err(unsupported(
"this install declares no vision tower (arch.vision.depth is 0)".to_string(),
));
}
let depth = usize::try_from(vision.depth).unwrap_or(0);
let hidden = usize::try_from(vision.hidden_size).unwrap_or(0);
let intermediate = usize::try_from(vision.intermediate_size).unwrap_or(0);
let heads = usize::try_from(vision.num_heads).unwrap_or(0);
let merge = usize::try_from(vision.spatial_merge_size).unwrap_or(0);
let out_hidden = usize::try_from(vision.out_hidden_size).unwrap_or(0);
let patch = usize::try_from(vision.patch_size).unwrap_or(0);
let temporal = usize::try_from(vision.temporal_patch_size).unwrap_or(0);
let channels = usize::try_from(vision.in_channels).unwrap_or(0);
let pos_rows = usize::try_from(vision.num_position_embeddings).unwrap_or(0);
for (label, value) in [
("depth", depth),
("hidden_size", hidden),
("intermediate_size", intermediate),
("num_heads", heads),
("spatial_merge_size", merge),
("out_hidden_size", out_hidden),
("patch_size", patch),
("temporal_patch_size", temporal),
("in_channels", channels),
("num_position_embeddings", pos_rows),
] {
if value == 0 {
return Err(unsupported(format!(
"vision config field {label} is zero, which describes no tower"
)));
}
}
if hidden % heads != 0 {
return Err(unsupported(format!(
"vision hidden_size {hidden} is not divisible by num_heads {heads}"
)));
}
let head_dim = hidden / heads;
if head_dim % 4 != 0 {
return Err(unsupported(format!(
"vision head_dim {head_dim} must be a multiple of 4 for the 2-D rope's two axes"
)));
}
if head_dim > gpu::MAX_ATTENTION_HEAD_DIM as usize {
return Err(unsupported(format!(
"vision head_dim {head_dim} exceeds the attention kernel's register tile \
({}); it would be truncated silently rather than refused",
gpu::MAX_ATTENTION_HEAD_DIM
)));
}
let pos_side = (pos_rows as f64).sqrt().round() as usize;
if pos_side * pos_side != pos_rows {
return Err(unsupported(format!(
"vision num_position_embeddings {pos_rows} is not a square grid; the bilinear \
interpolation indexes it by (h, w) and has no meaning otherwise"
)));
}
if vision.deepstack_visual_indexes.len() > MAX_VISION_DEEPSTACK_MERGERS {
return Err(unsupported(format!(
"deepstack_visual_indexes has {} entries; at most \
{MAX_VISION_DEEPSTACK_MERGERS} are supported",
vision.deepstack_visual_indexes.len()
)));
}
let mut deepstack = Vec::with_capacity(vision.deepstack_visual_indexes.len());
for &idx in &vision.deepstack_visual_indexes {
if idx < 0 || idx >= vision.depth {
return Err(unsupported(format!(
"deepstack_visual_indexes holds {idx}, outside this tower's depth of {}",
vision.depth
)));
}
deepstack.push(idx as usize);
}
Ok(Self {
depth,
hidden,
intermediate,
heads,
head_dim,
merge,
out_hidden,
patch_dim: temporal * patch * patch * channels,
pos_rows,
pos_side,
patch_size: patch,
deepstack,
})
}
}