1use crate::dit::Proj;
30use crate::pool::Pool;
31use cortiq_core::CmfModel;
32use std::sync::Arc;
33
34const GRID_SIDE: usize = 48;
36const EPS: f64 = 1e-6;
37
38struct Block {
39 n1_w: Vec<f32>,
40 n1_b: Vec<f32>,
41 n2_w: Vec<f32>,
42 n2_b: Vec<f32>,
43 qkv: Proj,
44 qkv_b: Vec<f32>,
45 proj: Proj,
46 proj_b: Vec<f32>,
47 fc1: Proj,
48 fc1_b: Vec<f32>,
49 fc2: Proj,
50 fc2_b: Vec<f32>,
51}
52
53struct Merger {
57 n_w: Vec<f32>,
58 n_b: Vec<f32>,
59 fc1: Proj,
60 fc1_b: Vec<f32>,
61 fc2: Proj,
62 fc2_b: Vec<f32>,
63 post_shuffle: bool,
64}
65
66pub struct VisionTower {
67 patch: Proj, patch_b: Vec<f32>,
69 pos: Vec<f32>, blocks: Vec<Block>,
71 merger: Merger,
72 deepstack: Vec<Merger>,
73 deepstack_at: Vec<usize>,
74 pool: Option<Arc<Pool>>,
75 pub hidden: usize,
76 pub out_hidden: usize,
77 heads: usize,
78 head_dim: usize,
79 pub patch_size: usize,
80 pub temporal_patch: usize,
81 pub merge: usize,
82}
83
84fn layer_norm(x: &[f32], w: &[f32], b: &[f32], dst: &mut [f32]) {
85 let n = x.len() as f64;
86 let mean = x.iter().map(|&v| v as f64).sum::<f64>() / n;
87 let var = x.iter().map(|&v| (v as f64 - mean).powi(2)).sum::<f64>() / n;
88 let inv = 1.0 / (var + EPS).sqrt();
89 for (((d, &v), &g), &bb) in dst.iter_mut().zip(x).zip(w).zip(b) {
90 *d = ((v as f64 - mean) * inv) as f32 * g + bb;
91 }
92}
93
94fn gelu_exact(v: f32) -> f32 {
96 0.5 * v * (1.0 + erf(v as f64 / std::f64::consts::SQRT_2) as f32)
97}
98
99fn gelu_tanh(v: f32) -> f32 {
101 const C: f32 = 0.797_884_6; 0.5 * v * (1.0 + (C * (v + 0.044715 * v * v * v)).tanh())
103}
104
105fn erf(x: f64) -> f64 {
112 let z = x.abs();
113 let t = 1.0 / (1.0 + 0.5 * z);
114 let ans = t
115 * (-z * z - 1.265_512_23
116 + t * (1.000_023_68
117 + t * (0.374_091_96
118 + t * (0.096_784_18
119 + t * (-0.186_288_06
120 + t * (0.278_868_07
121 + t * (-1.135_203_98
122 + t * (1.488_515_87
123 + t * (-0.822_152_23 + t * 0.170_872_77)))))))))
124 .exp();
125 if x >= 0.0 { 1.0 - ans } else { ans - 1.0 }
127}
128
129struct SendPtr(*mut f32);
130unsafe impl Send for SendPtr {}
131unsafe impl Sync for SendPtr {}
132impl SendPtr {
133 #[allow(clippy::mut_from_ref)]
135 unsafe fn row(&self, off: usize, len: usize) -> &mut [f32] {
136 unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
137 }
138}
139
140fn softmax_inplace(row: &mut [f32]) {
141 let mx = row.iter().cloned().fold(f32::MIN, f32::max);
142 let mut den = 0f32;
143 for r in row.iter_mut() {
144 *r = (*r - mx).exp();
145 den += *r;
146 }
147 if den > 0.0 {
148 let inv = 1.0 / den;
149 for r in row.iter_mut() {
150 *r *= inv;
151 }
152 }
153}
154
155impl Merger {
156 fn load(model: &Arc<CmfModel>, prefix: &str, post_shuffle: bool) -> Result<Self, String> {
157 let f = |n: &str| crate::dit::cmf_f32(model, n);
158 Ok(Self {
159 n_w: f(&format!("{prefix}.norm.weight"))?,
160 n_b: f(&format!("{prefix}.norm.bias"))?,
161 fc1: Proj::from_model(model, &format!("{prefix}.linear_fc1.weight"))?,
162 fc1_b: f(&format!("{prefix}.linear_fc1.bias"))?,
163 fc2: Proj::from_model(model, &format!("{prefix}.linear_fc2.weight"))?,
164 fc2_b: f(&format!("{prefix}.linear_fc2.bias"))?,
165 post_shuffle,
166 })
167 }
168
169 fn apply(
171 &self,
172 x: &[f32],
173 n: usize,
174 hidden: usize,
175 merge_dim: usize,
176 pool: Option<&Pool>,
177 ) -> Vec<f32> {
178 let groups = n * hidden / merge_dim;
179 let mut buf = vec![0f32; n * hidden];
180 if self.post_shuffle {
181 for g in 0..groups {
183 layer_norm(
184 &x[g * merge_dim..(g + 1) * merge_dim],
185 &self.n_w,
186 &self.n_b,
187 &mut buf[g * merge_dim..(g + 1) * merge_dim],
188 );
189 }
190 } else {
191 for p in 0..n {
193 layer_norm(
194 &x[p * hidden..(p + 1) * hidden],
195 &self.n_w,
196 &self.n_b,
197 &mut buf[p * hidden..(p + 1) * hidden],
198 );
199 }
200 }
201 let mut h = vec![0f32; groups * merge_dim];
202 self.fc1.matmat(&buf, groups, &mut h, pool);
203 for r in h.chunks_exact_mut(merge_dim) {
204 for (v, &b) in r.iter_mut().zip(&self.fc1_b) {
205 *v = gelu_exact(*v + b);
206 }
207 }
208 let out_dim = self.fc2.rows();
209 let mut out = vec![0f32; groups * out_dim];
210 self.fc2.matmat(&h, groups, &mut out, pool);
211 for r in out.chunks_exact_mut(out_dim) {
212 for (v, &b) in r.iter_mut().zip(&self.fc2_b) {
213 *v += b;
214 }
215 }
216 out
217 }
218}
219
220impl VisionTower {
221 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
222 let cfg: serde_json::Value = serde_json::from_slice(
223 model
224 .tensor_bytes("vis.config_json")
225 .map_err(|e| e.to_string())?,
226 )
227 .map_err(|e| format!("vis.config_json: {e}"))?;
228 let u = |k: &str, d: usize| cfg[k].as_u64().map(|v| v as usize).unwrap_or(d);
229 let n = u("depth", 27);
230 let f = |s: &str| crate::dit::cmf_f32(model, s);
231 let mut blocks = Vec::with_capacity(n);
232 for i in 0..n {
233 let p = format!("vis.blocks.{i}");
234 blocks.push(Block {
235 n1_w: f(&format!("{p}.norm1.weight"))?,
236 n1_b: f(&format!("{p}.norm1.bias"))?,
237 n2_w: f(&format!("{p}.norm2.weight"))?,
238 n2_b: f(&format!("{p}.norm2.bias"))?,
239 qkv: Proj::from_model(model, &format!("{p}.attn.qkv.weight"))?,
240 qkv_b: f(&format!("{p}.attn.qkv.bias"))?,
241 proj: Proj::from_model(model, &format!("{p}.attn.proj.weight"))?,
242 proj_b: f(&format!("{p}.attn.proj.bias"))?,
243 fc1: Proj::from_model(model, &format!("{p}.mlp.linear_fc1.weight"))?,
244 fc1_b: f(&format!("{p}.mlp.linear_fc1.bias"))?,
245 fc2: Proj::from_model(model, &format!("{p}.mlp.linear_fc2.weight"))?,
246 fc2_b: f(&format!("{p}.mlp.linear_fc2.bias"))?,
247 });
248 }
249 let deepstack_at: Vec<usize> = cfg["deepstack_visual_indexes"]
250 .as_array()
251 .map(|a| a.iter().map(|v| v.as_u64().unwrap_or(0) as usize).collect())
252 .unwrap_or_default();
253 let deepstack = (0..deepstack_at.len())
254 .map(|i| Merger::load(model, &format!("vis.deepstack.{i}"), true))
255 .collect::<Result<Vec<_>, _>>()?;
256 let hidden = u("hidden_size", 1152);
257 Ok(Self {
258 patch: Proj::from_model(model, "vis.patch_embed.weight")?,
259 patch_b: f("vis.patch_embed.bias")?,
260 pos: f("vis.pos_embed.weight")?,
261 blocks,
262 merger: Merger::load(model, "vis.merger", false)?,
263 deepstack,
264 deepstack_at,
265 pool: Pool::from_env(),
266 hidden,
267 out_hidden: u("out_hidden_size", 5120),
268 heads: u("num_heads", 16),
269 head_dim: hidden / u("num_heads", 16),
270 patch_size: u("patch_size", 16),
271 temporal_patch: u("temporal_patch_size", 2),
272 merge: u("spatial_merge_size", 2),
273 })
274 }
275
276 fn positions(&self, h: usize, w: usize) -> Vec<f32> {
279 let hd = self.hidden;
280 let m = self.merge;
281 let lin = |n: usize, i: usize| -> f64 {
282 if n == 1 {
283 0.0
284 } else {
285 i as f64 * (GRID_SIDE - 1) as f64 / (n - 1) as f64
286 }
287 };
288 let mut flat = vec![0f32; h * w * hd];
289 for y in 0..h {
290 let fy = lin(h, y);
291 let y0 = fy as usize;
292 let y1 = (y0 + 1).min(GRID_SIDE - 1);
293 let dy = (fy - y0 as f64) as f32;
294 for x in 0..w {
295 let fx = lin(w, x);
296 let x0 = fx as usize;
297 let x1 = (x0 + 1).min(GRID_SIDE - 1);
298 let dx = (fx - x0 as f64) as f32;
299 let dst = &mut flat[(y * w + x) * hd..(y * w + x + 1) * hd];
300 for (idx, wt) in [
301 (y0 * GRID_SIDE + x0, (1.0 - dy) * (1.0 - dx)),
302 (y0 * GRID_SIDE + x1, (1.0 - dy) * dx),
303 (y1 * GRID_SIDE + x0, dy * (1.0 - dx)),
304 (y1 * GRID_SIDE + x1, dy * dx),
305 ] {
306 let src = &self.pos[idx * hd..(idx + 1) * hd];
307 for (d, &s) in dst.iter_mut().zip(src) {
308 *d += s * wt;
309 }
310 }
311 }
312 }
313 let mut out = vec![0f32; h * w * hd];
315 let mut k = 0usize;
316 for by in 0..h / m {
317 for bx in 0..w / m {
318 for iy in 0..m {
319 for ix in 0..m {
320 let s = ((by * m + iy) * w + bx * m + ix) * hd;
321 out[k * hd..(k + 1) * hd].copy_from_slice(&flat[s..s + hd]);
322 k += 1;
323 }
324 }
325 }
326 }
327 out
328 }
329
330 fn rope_angles(&self, h: usize, w: usize) -> Vec<f32> {
333 let half = self.head_dim / 2; let k = half / 2; let inv: Vec<f64> = (0..k)
336 .map(|i| 1.0 / 10000f64.powf(2.0 * i as f64 / half as f64))
337 .collect();
338 let m = self.merge;
339 let mut out = Vec::with_capacity(h * w * half);
340 for by in 0..h / m {
341 for bx in 0..w / m {
342 for iy in 0..m {
343 for ix in 0..m {
344 let (r, c) = ((by * m + iy) as f64, (bx * m + ix) as f64);
345 for &f in &inv {
346 out.push((r * f) as f32);
347 }
348 for &f in &inv {
349 out.push((c * f) as f32);
350 }
351 }
352 }
353 }
354 }
355 out
356 }
357
358 fn attention(&self, qkv: &[f32], n: usize, angles: &[f32], out: &mut [f32]) {
359 let (nh, hd) = (self.heads, self.head_dim);
360 let inner = nh * hd;
361 let half = hd / 2;
362 let scale = 1.0 / (hd as f32).sqrt();
363 let pool = self.pool.as_deref();
364 let mut qh = vec![0f32; n * hd];
365 let mut kh = vec![0f32; n * hd];
366 let mut vt = vec![0f32; hd * n];
367 let mut scores = vec![0f32; n * n];
368 let mut oh = vec![0f32; n * hd];
369 for h in 0..nh {
370 for p in 0..n {
371 let base = p * 3 * inner;
374 let (qs, ks, vs) = (
375 &qkv[base + h * hd..base + (h + 1) * hd],
376 &qkv[base + inner + h * hd..base + inner + (h + 1) * hd],
377 &qkv[base + 2 * inner + h * hd..base + 2 * inner + (h + 1) * hd],
378 );
379 let ang = &angles[p * half..(p + 1) * half];
380 for j in 0..half {
383 let (s, c) = ang[j].sin_cos();
384 qh[p * hd + j] = (qs[j] * c - qs[j + half] * s) * scale;
385 qh[p * hd + j + half] = (qs[j] * s + qs[j + half] * c) * scale;
386 kh[p * hd + j] = ks[j] * c - ks[j + half] * s;
387 kh[p * hd + j + half] = ks[j] * s + ks[j + half] * c;
388 }
389 for (d, &val) in vs.iter().enumerate() {
390 vt[d * n + p] = val;
391 }
392 }
393 crate::fcd_ops::gemm_nt(&qh, &kh, &mut scores, n, hd, n, pool);
394 let sp = SendPtr(scores.as_mut_ptr());
395 let soft = |lo: usize, hi: usize| {
396 for r in lo..hi {
397 softmax_inplace(unsafe { sp.row(r * n, n) });
399 }
400 };
401 match pool {
402 Some(p) => p.run_rows(n, &soft),
403 None => soft(0, n),
404 }
405 crate::fcd_ops::gemm_nt(&scores, &vt, &mut oh, n, n, hd, pool);
406 for p in 0..n {
407 out[p * inner + h * hd..p * inner + (h + 1) * hd]
408 .copy_from_slice(&oh[p * hd..(p + 1) * hd]);
409 }
410 }
411 }
412
413 pub fn forward(&self, patches: &[f32], h: usize, w: usize) -> (Vec<f32>, Vec<Vec<f32>>) {
416 let n = h * w;
417 let hd = self.hidden;
418 let pool = self.pool.as_deref();
419 let mut x = vec![0f32; n * hd];
420 self.patch.matmat(patches, n, &mut x, pool);
421 let pos = self.positions(h, w);
422 for (i, v) in x.iter_mut().enumerate() {
423 *v += self.patch_b[i % hd] + pos[i];
424 }
425 let angles = self.rope_angles(h, w);
426
427 let inner = self.heads * self.head_dim;
428 let inter = self.blocks[0].fc1.rows();
429 let mut xn = vec![0f32; n * hd];
430 let mut qkv = vec![0f32; n * 3 * inner];
431 let mut attn = vec![0f32; n * inner];
432 let mut proj = vec![0f32; n * hd];
433 let mut ff = vec![0f32; n * inter];
434 let merge_dim = hd * self.merge * self.merge;
435 let mut deep = Vec::new();
436 for (li, blk) in self.blocks.iter().enumerate() {
437 for (o, s) in xn.chunks_exact_mut(hd).zip(x.chunks_exact(hd)) {
438 layer_norm(s, &blk.n1_w, &blk.n1_b, o);
439 }
440 blk.qkv.matmat(&xn, n, &mut qkv, pool);
441 for r in qkv.chunks_exact_mut(3 * inner) {
442 for (v, &b) in r.iter_mut().zip(&blk.qkv_b) {
443 *v += b;
444 }
445 }
446 self.attention(&qkv, n, &angles, &mut attn);
447 blk.proj.matmat(&attn, n, &mut proj, pool);
448 for (p, r) in proj.chunks_exact(hd).enumerate() {
449 for (i, &v) in r.iter().enumerate() {
450 x[p * hd + i] += v + blk.proj_b[i];
451 }
452 }
453 for (o, s) in xn.chunks_exact_mut(hd).zip(x.chunks_exact(hd)) {
454 layer_norm(s, &blk.n2_w, &blk.n2_b, o);
455 }
456 blk.fc1.matmat(&xn, n, &mut ff, pool);
457 for r in ff.chunks_exact_mut(inter) {
458 for (v, &b) in r.iter_mut().zip(&blk.fc1_b) {
459 *v = gelu_tanh(*v + b);
460 }
461 }
462 blk.fc2.matmat(&ff, n, &mut proj, pool);
463 for (p, r) in proj.chunks_exact(hd).enumerate() {
464 for (i, &v) in r.iter().enumerate() {
465 x[p * hd + i] += v + blk.fc2_b[i];
466 }
467 }
468 if let Some(k) = self.deepstack_at.iter().position(|&d| d == li) {
469 deep.push(self.deepstack[k].apply(&x, n, hd, merge_dim, pool));
470 }
471 }
472 (self.merger.apply(&x, n, hd, merge_dim, pool), deep)
473 }
474}
475
476pub fn preprocess(
484 rgb: &[f32],
485 h: usize,
486 w: usize,
487 patch: usize,
488 temporal: usize,
489 merge: usize,
490) -> (Vec<f32>, usize, usize) {
491 let factor = patch * merge;
492 let hb = ((h as f64 / factor as f64).round() as usize).max(1) * factor;
493 let wb = ((w as f64 / factor as f64).round() as usize).max(1) * factor;
494 let mut img = vec![0f32; 3 * hb * wb];
496 for c in 0..3 {
497 for y in 0..hb {
498 let sy = ((y as f64 + 0.5) * h as f64 / hb as f64 - 0.5).max(0.0);
499 let y0 = sy.floor() as usize;
500 let y1 = (y0 + 1).min(h - 1);
501 let fy = (sy - y0 as f64) as f32;
502 for x in 0..wb {
503 let sx = ((x as f64 + 0.5) * w as f64 / wb as f64 - 0.5).max(0.0);
504 let x0 = sx.floor() as usize;
505 let x1 = (x0 + 1).min(w - 1);
506 let fx = (sx - x0 as f64) as f32;
507 let p = |yy: usize, xx: usize| rgb[(c * h + yy) * w + xx];
508 let top = p(y0, x0) * (1.0 - fx) + p(y0, x1) * fx;
509 let bot = p(y1, x0) * (1.0 - fx) + p(y1, x1) * fx;
510 img[(c * hb + y) * wb + x] = (top * (1.0 - fy) + bot * fy - 0.5) / 0.5;
511 }
512 }
513 }
514 let (gh, gw) = (hb / patch, wb / patch);
515 let per = 3 * temporal * patch * patch;
516 let mut out = vec![0f32; gh * gw * per];
517 let mut k = 0usize;
524 for bby in 0..gh / merge {
525 for bbx in 0..gw / merge {
526 for iy in 0..merge {
527 for ix in 0..merge {
528 let (by, bx) = (bby * merge + iy, bbx * merge + ix);
529 let dst = &mut out[k * per..(k + 1) * per];
530 k += 1;
531 let mut j = 0;
532 for c in 0..3 {
533 for _t in 0..temporal {
534 for y in 0..patch {
535 for x in 0..patch {
536 dst[j] = img[(c * hb + by * patch + y) * wb + bx * patch + x];
537 j += 1;
538 }
539 }
540 }
541 }
542 }
543 }
544 }
545 }
546 (out, gh, gw)
547}
548
549#[cfg(test)]
550mod tests {
551 use super::*;
552
553 #[test]
554 fn the_two_gelus_are_not_the_same_function() {
555 assert!((gelu_exact(0.0)).abs() < 1e-12);
558 assert!((gelu_exact(1.0) - 0.841_344_8).abs() < 2e-7);
559 assert!((gelu_tanh(1.0) - 0.841_192).abs() < 1e-5);
560 assert!((gelu_exact(1.0) - gelu_tanh(1.0)).abs() > 1e-5);
561 assert!((gelu_exact(-3.0) - -0.004_049_5).abs() < 2e-6);
562 }
563
564 #[test]
565 fn erf_is_accurate_across_the_range() {
566 for (x, want) in [
567 (0.0, 0.0),
568 (0.25, 0.276_326_390_168_236_9),
569 (0.5, 0.520_499_877_813_046_5),
570 (1.0, 0.842_700_792_949_714_9),
571 (2.0, 0.995_322_265_018_952_7),
572 (3.5, 0.999_999_256_901_627_7),
573 ] {
574 let got = erf(x);
575 assert!((got - want).abs() < 2e-7, "erf({x}) = {got}, want {want}");
576 assert!((erf(-x) + want).abs() < 2e-7, "erf is odd");
577 }
578 }
579
580 #[test]
581 fn preprocess_rounds_the_canvas_to_the_merge_factor() {
582 let (h, w) = (70usize, 100usize);
583 let rgb = vec![0.5f32; 3 * h * w];
584 let (p, gh, gw) = preprocess(&rgb, h, w, 16, 2, 2);
585 assert_eq!((gh, gw), (4, 6));
587 assert_eq!(p.len(), gh * gw * 3 * 2 * 16 * 16);
588 assert!(p.iter().all(|&v| v.abs() < 1e-6));
590 }
591}