1use std::sync::Arc;
13
14use anyhow::{anyhow, Context, Result};
15use cortiq_core::quant::dequant_tensor;
16use cortiq_core::{CmfModel, TensorDtype};
17
18use crate::inference::gelu_erf;
19use crate::pool::Pool;
20use crate::qtensor::QTensor;
21
22const NEG: f32 = -1e30;
24
25pub fn preprocess(frame: &crate::media::RgbFrame) -> Vec<f32> {
30 use image::imageops::{self, FilterType};
31 let img = image::RgbImage::from_raw(
32 frame.width as u32,
33 frame.height as u32,
34 frame.data.clone(),
35 )
36 .expect("RgbFrame buffer matches its own dimensions");
37 let rs = imageops::resize(&img, PIX as u32, PIX as u32, FilterType::Triangle);
38 let raw = rs.into_raw();
39 let mut out = vec![0f32; 3 * PIX * PIX];
40 for i in 0..PIX * PIX {
41 for c in 0..3 {
42 out[c * PIX * PIX + i] = (raw[i * 3 + c] as f32 / 255.0 - 0.5) / 0.5;
43 }
44 }
45 out
46}
47
48const PIX: usize = 1008;
49const PATCH: usize = 14;
50const GRID: usize = PIX / PATCH; const WIN: usize = 24;
52const TD: usize = 1024;
53const TH: usize = 16;
54const TL: usize = 32;
55const TI: usize = 4736;
56const T_EPS: f32 = 1e-6;
57const GLOBAL: [usize; 4] = [7, 15, 23, 31];
58
59const SS: usize = 32;
60const SD: usize = 1024;
61const SL: usize = 24;
62const SI: usize = 4096;
63const S_EPS: f32 = 1e-5;
64const PROJ: usize = 512;
65
66const FD: usize = 256;
67const FI: usize = 2048;
68const FH: usize = 8;
69const D_EPS: f32 = 1e-6;
70const NL: usize = 6;
71const NQ: usize = 200;
72const MH: usize = 288;
73
74pub struct Sam3Out {
76 pub text_embeds: Vec<f32>,
78 pub text_feats: Vec<f32>,
80 pub trunk_feats: Vec<f32>,
82 pub fpn: Vec<Vec<f32>>,
84 pub enc_hidden: Vec<f32>,
86 pub dec_hidden: Vec<f32>,
88 pub pred_boxes: Vec<f32>,
90 pub pred_masks: Vec<f32>,
92 pub pred_logits: Vec<f32>,
98 pub presence_logit: f32,
101}
102
103#[derive(Clone, Copy)]
105pub(crate) enum Mask<'a> {
106 None,
107 Key(&'a [f32]),
109 Add(&'a [f32]),
111 Head(&'a [f32]),
113}
114
115pub struct Sam3 {
116 model: Arc<CmfModel>,
117 pool: Option<Arc<Pool>>,
118 fpn_prefix: &'static str,
120 pos_skip_cls: bool,
123}
124
125impl Sam3 {
126 pub fn open(path: &str) -> Result<Self> {
127 let model = CmfModel::open(path)
128 .with_context(|| format!("open {path}"))?
129 .into();
130 let fpn_prefix = match std::env::var("SAM3_FPN").as_deref() {
131 Ok("sam2_convs") => "fpn.sam2_convs",
132 _ => "fpn.convs",
133 };
134 let pos_skip_cls = std::env::var("SAM3_POS").as_deref() != Ok("first");
135 Ok(Self { model, pool: Pool::from_env(), fpn_prefix, pos_skip_cls })
136 }
137
138 pub(crate) fn cmf(&self) -> &Arc<CmfModel> { &self.model }
141
142 pub(crate) fn pool_handle(&self) -> Option<&Arc<Pool>> { self.pool.as_ref() }
144
145 pub fn directory(&self) -> Vec<String> {
148 self.model
149 .tensors
150 .iter()
151 .map(|t| format!("{} {:?} {:?}", t.name, t.dtype, t.shape))
152 .collect()
153 }
154
155 pub fn raw_tensor(&self, name: &str) -> Option<Vec<u8>> {
157 let e = self.model.tensor(name)?;
158 Some(self.model.entry_bytes(e).to_vec())
159 }
160
161 pub fn bad_scales(&self) -> Vec<String> {
164 let f16_bad = |h: u16| h & 0x7C00 == 0x7C00;
165 let mut out = Vec::new();
166 for t in self.model.tensors.iter() {
167 let bytes = self.model.entry_bytes(t);
168 match t.dtype {
169 TensorDtype::Q8Row if t.shape.len() == 2 => {
170 let (rows, cols) = (t.shape[0], t.shape[1]);
171 let off = rows * cols;
172 if bytes.len() < off + rows * 2 {
173 out.push(format!("{}@payload short", t.name));
174 continue;
175 }
176 for r in 0..rows {
177 let h = u16::from_le_bytes([bytes[off + r * 2], bytes[off + r * 2 + 1]]);
178 if f16_bad(h) {
179 out.push(format!("{}@row{r} scale_f16=0x{h:04x}", t.name));
180 }
181 }
182 }
183 TensorDtype::F16 => {
184 for (i, c) in bytes.chunks_exact(2).enumerate() {
185 let h = u16::from_le_bytes([c[0], c[1]]);
186 if f16_bad(h) {
187 out.push(format!("{}@{i} val_f16=0x{h:04x}", t.name));
188 if out.len() > 200 { return out; }
189 }
190 }
191 }
192 _ => {}
193 }
194 }
195 out
196 }
197
198 pub fn forward(&mut self, pixels: &[f32], ids: &[u32], attn: &[u8]) -> Result<Sam3Out> {
208 if pixels.len() != 3 * PIX * PIX {
209 return Err(anyhow!("pixels {} != 3*{}*{}", pixels.len(), PIX, PIX));
210 }
211 if ids.len() != SS || attn.len() != SS {
212 return Err(anyhow!("ids/attn must be {SS} long"));
213 }
214 let only = std::env::var("SAM3_ONLY").ok();
215 let want = |k: &str| {
216 only
217 .as_deref()
218 .map_or(true, |o| o.split(',').any(|s| s.trim() == k))
219 };
220 let pool = self.pool.as_deref();
221 let (text_embeds, text_feats) = self.text(ids, attn, pool)?;
222 let blank = |te: Vec<f32>, tf: Vec<f32>| Sam3Out {
223 text_embeds: te,
224 text_feats: tf,
225 trunk_feats: Vec::new(),
226 fpn: Vec::new(),
227 enc_hidden: Vec::new(),
228 dec_hidden: Vec::new(),
229 pred_boxes: Vec::new(),
230 pred_masks: Vec::new(),
231 pred_logits: Vec::new(),
232 presence_logit: 0.0,
233 };
234 if !want("trunk") {
235 return Ok(blank(text_embeds, text_feats));
236 }
237 let trunk_feats = self.trunk(pixels, pool)?;
238 if !want("fpn") {
239 let mut o = blank(text_embeds, text_feats);
240 o.trunk_feats = trunk_feats;
241 return Ok(o);
242 }
243 let fpn = self.fpn(&trunk_feats, pool)?;
244 let pos = sine_pos(GRID, GRID, FD / 2); let vision = chw_to_rows(&fpn[2], GRID, GRID);
246 let enc_hidden = self.detr_encoder(&vision, &pos, &text_feats, attn, pool)?;
247 if !want("dec") {
248 let mut o = blank(text_embeds, text_feats);
249 o.trunk_feats = trunk_feats;
250 o.fpn = fpn;
251 o.enc_hidden = enc_hidden;
252 return Ok(o);
253 }
254 let (dec_hidden, final_refs, presence_logit) =
255 self.detr_decoder(&enc_hidden, &pos, &text_feats, attn, pool)?;
256 let pred_logits = self.score(
261 &text_feats,
262 attn,
263 &dec_hidden[(NL - 1) * NQ * FD..],
264 pool,
265 )?;
266 if !want("masks") {
267 let mut o = blank(text_embeds, text_feats);
268 o.trunk_feats = trunk_feats;
269 o.fpn = fpn;
270 o.enc_hidden = enc_hidden;
271 o.dec_hidden = dec_hidden.clone();
272 o.pred_boxes = xyxy(&final_refs);
273 o.pred_logits = pred_logits;
274 o.presence_logit = presence_logit;
275 return Ok(o);
276 }
277 let pred_boxes = xyxy(&final_refs);
278 let pred_masks = self.mask_decoder(
279 &dec_hidden[(NL - 1) * NQ * FD..],
280 &enc_hidden,
281 &fpn,
282 &text_feats,
283 attn,
284 pool,
285 )?;
286 Ok(Sam3Out {
287 text_embeds,
288 text_feats,
289 trunk_feats,
290 fpn,
291 enc_hidden,
292 dec_hidden,
293 pred_boxes,
294 pred_masks,
295 pred_logits,
296 presence_logit,
297 })
298 }
299
300 fn text(&self, ids: &[u32], attn: &[u8], pool: Option<&Pool>) -> Result<(Vec<f32>, Vec<f32>)> {
303 let m = &self.model;
304 let tok = lin(m, "text.encoder.token_embedding.weight")?;
305 let pos = load_vec(m, "text.encoder.positional_embedding")?;
306 let mut x = vec![0f32; SS * SD];
307 let mut row = vec![0f32; SD];
308 for (i, &id) in ids.iter().enumerate() {
309 tok.row_f32(id as usize, &mut row);
310 let r = &mut x[i * SD..(i + 1) * SD];
311 r.copy_from_slice(&row);
312 for (v, p) in r.iter_mut().zip(&pos[i * SD..(i + 1) * SD]) {
313 *v += *p;
314 }
315 }
316 dbg_stats("text.emb", &x);
317 let mut cm = vec![0f32; SS * SS];
319 for q in 0..SS {
320 for k in 0..=q {
321 if attn[k] == 0 {
322 cm[q * SS + k] = NEG;
323 }
324 }
325 for k in (q + 1)..SS {
326 cm[q * SS + k] = NEG;
327 }
328 }
329 for l in 0..SL {
330 let b = format!("text.encoder.transformer.resblocks.{l}.");
331 let (lw, lb) = (load_vec(m, &format!("{b}ln_1.weight"))?, load_vec(m, &format!("{b}ln_1.bias"))?);
332 let mut h = x.clone();
334 layer_norm_rows(&mut h, &lw, &lb, S_EPS);
335 let qkv = lin(m, &format!("{b}attn.in_proj_weight"))?;
336 let ib = load_vec(m, &format!("{b}attn.in_proj_bias"))?;
337 let op = lin(m, &format!("{b}attn.out_proj.weight"))?;
338 let ob = load_vec(m, &format!("{b}attn.out_proj.bias"))?;
339 let mut ao = vec![0f32; SS * SD];
340 run_attn(
341 &qkv, &ib, &op, &ob,
342 &h, &h, &h, SS, SS, SD, 16, None, Mask::Add(&cm), pool, &mut ao,
343 );
344 for (a, c) in x.iter_mut().zip(&ao) { *a += *c; }
345 let (lw, lb) = (load_vec(m, &format!("{b}ln_2.weight"))?, load_vec(m, &format!("{b}ln_2.bias"))?);
346 let mut h = x.clone();
347 layer_norm_rows(&mut h, &lw, &lb, S_EPS);
348 let f1 = lin(m, &format!("{b}mlp.c_fc.weight"))?;
349 let f1b = load_vec(m, &format!("{b}mlp.c_fc.bias"))?;
350 let f2 = lin(m, &format!("{b}mlp.c_proj.weight"))?;
351 let f2b = load_vec(m, &format!("{b}mlp.c_proj.bias"))?;
352 let mut ff = vec![0f32; SS * SI];
353 f1.matmat(&h, SS, &mut ff, pool);
354 add_bias(&mut ff, SI, &f1b);
355 ff.iter_mut().for_each(|v| *v = gelu_erf(*v));
356 let mut fo = vec![0f32; SS * SD];
357 f2.matmat(&ff, SS, &mut fo, pool);
358 add_bias(&mut fo, SD, &f2b);
359 for (a, c) in x.iter_mut().zip(&fo) { *a += *c; }
360 dbg_stats(&format!("text.l{l}"), &x);
361 }
362 let (lw, lb) = (load_vec(m, "text.encoder.ln_final.weight")?, load_vec(m, "text.encoder.ln_final.bias")?);
363 layer_norm_rows(&mut x, &lw, &lb, S_EPS);
364 dbg_stats("text.ln_final", &x);
365 if let Some(d) = std::env::var("SAM3_DUMP_DIR").ok() {
366 dump_f32(&d, "text_lh", &x);
367 }
368 let p = ids.iter().position(|&v| v == 49407).ok_or_else(|| anyhow!("no eos token"))?;
370 let pooled = &x[p * SD..(p + 1) * SD];
371 let proj = load_vec(m, "text.encoder.text_projection")?;
373 dbg_stats("text.proj", &proj);
374 let mut embeds = vec![0f32; PROJ];
375 for j in 0..PROJ {
376 let mut acc = 0f32;
377 for i in 0..SD { acc += pooled[i] * proj[i * PROJ + j]; }
378 embeds[j] = acc;
379 }
380 dbg_stats("text.pooled", pooled);
381 dbg_stats("text.embeds", &embeds);
382 let rz = lin(m, "text.resizer.weight")?;
384 let rb = load_vec(m, "text.resizer.bias")?;
385 let mut feats = vec![0f32; SS * FD];
386 rz.matmat(&x, SS, &mut feats, pool);
387 add_bias(&mut feats, FD, &rb);
388 dbg_stats("text.feats", &feats);
389 Ok((embeds, feats))
390 }
391
392 fn trunk(&self, pixels: &[f32], pool: Option<&Pool>) -> Result<Vec<f32>> {
395 let m = &self.model;
396 let np = GRID * GRID;
397 let kc = 3 * PATCH * PATCH;
398 let dump = std::env::var("SAM3_DUMP_DIR").ok();
399 let dlayers: Option<Vec<usize>> = std::env::var("SAM3_DUMP_LAYERS")
400 .ok()
401 .map(|s| s.split(',').filter_map(|t| t.trim().parse().ok()).collect());
402 let w = load_vec(m, "trunk.patch_embed.proj.weight")?; let mut pv = vec![0f32; np * kc];
404 let pvb = pv.as_mut_ptr() as usize;
405 let fill = |s: usize, e: usize| {
406 let pv = unsafe { std::slice::from_raw_parts_mut(pvb as *mut f32, np * kc) };
407 for pt in s..e {
408 let (py, px) = (pt / GRID, pt % GRID);
409 let d = &mut pv[pt * kc..(pt + 1) * kc];
410 let mut o = 0;
411 for c in 0..3 {
412 for ky in 0..PATCH {
413 let y = py * PATCH + ky;
414 for kx in 0..PATCH {
415 d[o] = pixels[c * PIX * PIX + y * PIX + px * PATCH + kx];
416 o += 1;
417 }
418 }
419 }
420 }
421 };
422 match pool { Some(p) => p.run_rows(np, &fill), None => fill(0, np) };
423 let mut x = vec![0f32; np * TD];
424 let xb = x.as_mut_ptr() as usize;
425 let gemm = |s: usize, e: usize| {
426 let x = unsafe { std::slice::from_raw_parts_mut(xb as *mut f32, np * TD) };
427 for ch in s..e {
428 let wr = &w[ch * kc..(ch + 1) * kc];
429 for pt in 0..np {
430 let xv = &pv[pt * kc..(pt + 1) * kc];
431 let mut acc = 0f32;
432 for k in 0..kc { acc += wr[k] * xv[k]; }
433 x[pt * TD + ch] = acc;
434 }
435 }
436 };
437 match pool { Some(p) => p.run_rows(TD, &gemm), None => gemm(0, TD) };
438 if let Some(d) = &dump { dump_f32(d, "trunk_patch", &x); }
439 let pos = load_vec(m, "trunk.pos_embed")?;
441 let base = if self.pos_skip_cls { 1 } else { 0 };
442 if pos.len() < (base + 576) * TD { return Err(anyhow!("pos_embed too small")); }
443 for pt in 0..np {
444 let (py, px) = (pt / GRID, pt % GRID);
445 let src = (base + (py % 24) * 24 + px % 24) * TD;
446 for d in 0..TD { x[pt * TD + d] += pos[src + d]; }
447 }
448 let (lw, lb) = (load_vec(m, "trunk.ln_pre.weight")?, load_vec(m, "trunk.ln_pre.bias")?);
449 layer_norm_rows(&mut x, &lw, &lb, T_EPS);
450 if let Some(d) = &dump { dump_f32(d, "trunk_pre", &x); }
451
452 for l in 0..TL {
453 let b = format!("trunk.blocks.{l}.");
454 let (nw, nb) = (load_vec(m, &format!("{b}norm1.weight"))?, load_vec(m, &format!("{b}norm1.bias"))?);
455 let mut h = x.clone();
456 layer_norm_rows(&mut h, &nw, &nb, T_EPS);
457 let dbg0 = dump.is_some() && l == 0;
458 if let (Some(d), true) = (&dump, dbg0) { dump_f32(d, "b0_ln1", &h); }
459 let qkv = lin(m, &format!("{b}attn.qkv.weight"))?;
460 let qb = load_vec(m, &format!("{b}attn.qkv.bias"))?;
461 let proj = lin(m, &format!("{b}attn.proj.weight"))?;
462 let pb = load_vec(m, &format!("{b}attn.proj.bias"))?;
463 let fc = load_vec(m, &format!("{b}attn.freqs_cis"))?;
464 let attn_out = if GLOBAL.contains(&l) {
465 let mut o = vec![0f32; np * TD];
466 run_attn(&qkv, &qb, &proj, &pb, &h, &h, &h, np, np, TD, TH, Some(&fc), Mask::None, pool, &mut o);
467 o
468 } else {
469 self.vit_windows(&h, &qkv, &qb, &proj, &pb, &fc, pool)?
470 };
471 if let (Some(d), true) = (&dump, dbg0) { dump_f32(d, "b0_attn", &attn_out); }
472 for (a, c) in x.iter_mut().zip(&attn_out) { *a += *c; }
473 if let (Some(d), true) = (&dump, dbg0) { dump_f32(d, "b0_x2", &x); }
474 let (nw, nb) = (load_vec(m, &format!("{b}norm2.weight"))?, load_vec(m, &format!("{b}norm2.bias"))?);
478 let mut h = x.clone();
479 layer_norm_rows(&mut h, &nw, &nb, T_EPS);
480 if let (Some(d), true) = (&dump, dbg0) { dump_f32(d, "b0_ln2", &h); }
481 let f1 = lin(m, &format!("{b}mlp.fc1.weight"))?;
482 let f1b = load_vec(m, &format!("{b}mlp.fc1.bias"))?;
483 let f2 = lin(m, &format!("{b}mlp.fc2.weight"))?;
484 let f2b = load_vec(m, &format!("{b}mlp.fc2.bias"))?;
485 let mut ff = vec![0f32; np * TI];
486 f1.matmat(&h, np, &mut ff, pool);
487 add_bias(&mut ff, TI, &f1b);
488 ff.iter_mut().for_each(|v| *v = gelu_erf(*v));
489 let mut fo = vec![0f32; np * TD];
490 f2.matmat(&ff, np, &mut fo, pool);
491 add_bias(&mut fo, TD, &f2b);
492 if let (Some(d), true) = (&dump, dbg0) { dump_f32(d, "b0_mlp", &fo); }
493 for (a, c) in x.iter_mut().zip(&fo) { *a += *c; }
494 if let (Some(d), true) = (&dump, dlayers.as_ref().map_or(true, |ls| ls.contains(&l))) {
495 dump_f32(d, &format!("trunk_b{l}"), &x);
496 }
497 }
498 Ok(x)
499 }
500
501 fn vit_windows(
503 &self,
504 h: &[f32],
505 qkv: &QTensor,
506 qb: &[f32],
507 proj: &QTensor,
508 pb: &[f32],
509 fc: &[f32],
510 pool: Option<&Pool>,
511 ) -> Result<Vec<f32>> {
512 let np = GRID * GRID;
513 let nw = GRID / WIN;
514 let wl = WIN * WIN;
515 let mut wh = vec![0f32; wl * TD];
516 let mut ao = vec![0f32; wl * TD];
517 let mut out = vec![0f32; np * TD];
518 for wy in 0..nw {
519 for wx in 0..nw {
520 for y in 0..WIN {
521 for x2 in 0..WIN {
522 let s = ((wy * WIN + y) * GRID + wx * WIN + x2) * TD;
523 let d = (y * WIN + x2) * TD;
524 wh[d..d + TD].copy_from_slice(&h[s..s + TD]);
525 }
526 }
527 run_attn(qkv, qb, proj, pb, &wh, &wh, &wh, wl, wl, TD, TH, Some(fc), Mask::None, pool, &mut ao);
528 for y in 0..WIN {
529 for x2 in 0..WIN {
530 let d = ((wy * WIN + y) * GRID + wx * WIN + x2) * TD;
531 let s = (y * WIN + x2) * TD;
532 out[d..d + TD].copy_from_slice(&ao[s..s + TD]);
533 }
534 }
535 }
536 }
537 Ok(out)
538 }
539
540 fn fpn(&self, trunk: &[f32], pool: Option<&Pool>) -> Result<Vec<Vec<f32>>> {
545 fpn_levels(&self.model, self.fpn_prefix, trunk, pool, 4)
546 }
547
548 #[allow(clippy::too_many_arguments)]
551 fn detr_encoder(
552 &self,
553 vision: &[f32],
554 pos: &[f32],
555 text: &[f32],
556 am: &[u8],
557 pool: Option<&Pool>,
558 ) -> Result<Vec<f32>> {
559 let m = &self.model;
560 let n = vision.len() / FD;
561 let mut xs = vision.to_vec();
562 let mut km = vec![0f32; SS];
563 for (k, &v) in am.iter().enumerate() { km[k] = if v == 0 { NEG } else { 0.0 }; }
564 for l in 0..NL {
565 let b = format!("det.transformer.encoder.layers.{l}.");
566 let (nw, nb) = (load_vec(m, &format!("{b}norm1.weight"))?, load_vec(m, &format!("{b}norm1.bias"))?);
568 let mut h = xs.clone();
569 layer_norm_rows(&mut h, &nw, &nb, D_EPS);
570 let hp: Vec<f32> = h
571 .chunks_exact(FD)
572 .zip(pos.chunks_exact(FD))
573 .flat_map(|(a, c)| a.iter().zip(c).map(|(x, y)| x + y))
574 .collect();
575 let qkv = lin(m, &format!("{b}self_attn.in_proj_weight"))?;
576 let ib = load_vec(m, &format!("{b}self_attn.in_proj_bias"))?;
577 let op = lin(m, &format!("{b}self_attn.out_proj.weight"))?;
578 let ob = load_vec(m, &format!("{b}self_attn.out_proj.bias"))?;
579 let mut ao = vec![0f32; n * FD];
580 run_attn(&qkv, &ib, &op, &ob, &hp, &hp, &h, n, n, FD, FH, None, Mask::None, pool, &mut ao);
581 for (a, c) in xs.iter_mut().zip(&ao) { *a += *c; }
582 let (nw, nb) = (load_vec(m, &format!("{b}norm2.weight"))?, load_vec(m, &format!("{b}norm2.bias"))?);
584 let mut h = xs.clone();
585 layer_norm_rows(&mut h, &nw, &nb, D_EPS);
586 let qkv = lin(m, &format!("{b}cross_attn_image.in_proj_weight"))?;
587 let ib = load_vec(m, &format!("{b}cross_attn_image.in_proj_bias"))?;
588 let op = lin(m, &format!("{b}cross_attn_image.out_proj.weight"))?;
589 let ob = load_vec(m, &format!("{b}cross_attn_image.out_proj.bias"))?;
590 let mut ao = vec![0f32; n * FD];
591 run_attn(&qkv, &ib, &op, &ob, &h, text, text, n, SS, FD, FH, None, Mask::Key(&km), pool, &mut ao);
592 for (a, c) in xs.iter_mut().zip(&ao) { *a += *c; }
593 let (nw, nb) = (load_vec(m, &format!("{b}norm3.weight"))?, load_vec(m, &format!("{b}norm3.bias"))?);
595 let mut h = xs.clone();
596 layer_norm_rows(&mut h, &nw, &nb, D_EPS);
597 let l1 = lin(m, &format!("{b}linear1.weight"))?;
598 let l1b = load_vec(m, &format!("{b}linear1.bias"))?;
599 let l2 = lin(m, &format!("{b}linear2.weight"))?;
600 let l2b = load_vec(m, &format!("{b}linear2.bias"))?;
601 let mut mid = vec![0f32; n * FI];
602 l1.matmat(&h, n, &mut mid, pool);
603 add_bias(&mut mid, FI, &l1b);
604 mid.iter_mut().for_each(|v| *v = v.max(0.0));
605 let mut fo = vec![0f32; n * FD];
606 l2.matmat(&mid, n, &mut fo, pool);
607 add_bias(&mut fo, FD, &l2b);
608 for (a, c) in xs.iter_mut().zip(&fo) { *a += *c; }
609 }
610 Ok(xs)
611 }
612
613 fn detr_decoder(
622 &self,
623 enc: &[f32],
624 pos: &[f32],
625 text: &[f32],
626 am: &[u8],
627 pool: Option<&Pool>,
628 ) -> Result<(Vec<f32>, Vec<[f32; 4]>, f32)> {
629 let m = &self.model;
630 let n = enc.len() / FD;
631 let db = "det.transformer.decoder.";
632 let query = load_vec(m, &format!("{db}query_embed.weight"))?;
633 let presence = load_vec(m, &format!("{db}presence_token.weight"))?;
634 let ref0 = load_vec(m, &format!("{db}reference_points.weight"))?;
635 let mut hidden = vec![0f32; (NQ + 1) * FD];
636 hidden[..FD].copy_from_slice(&presence);
637 hidden[FD..].copy_from_slice(&query);
638 let mut refs: Vec<[f32; 4]> = ref0
639 .chunks_exact(4)
640 .map(|c| [sigm(c[0]), sigm(c[1]), sigm(c[2]), sigm(c[3])])
641 .collect();
642 let mut km = vec![0f32; SS];
643 for (k, &v) in am.iter().enumerate() { km[k] = if v == 0 { NEG } else { 0.0 }; }
644 let rh0 = lin(m, &format!("{db}ref_point_head.layers.0.weight"))?;
646 let rh0b = load_vec(m, &format!("{db}ref_point_head.layers.0.bias"))?;
647 let rh1 = lin(m, &format!("{db}ref_point_head.layers.1.weight"))?;
648 let rh1b = load_vec(m, &format!("{db}ref_point_head.layers.1.bias"))?;
649 let rx = DecoderMlp::load(m, &format!("{db}boxRPB_embed_x."))?;
650 let ry = DecoderMlp::load(m, &format!("{db}boxRPB_embed_y."))?;
651 let bbox = DecoderMlp3::load3(m, &format!("{db}bbox_embed."))?;
652 let olnw = load_vec(m, &format!("{db}norm.weight"))?;
653 let olnb = load_vec(m, &format!("{db}norm.bias"))?;
654 let pres_w = load_vec(m, &format!("{db}presence_token_out_norm.weight"))?;
655 let pres_b = load_vec(m, &format!("{db}presence_token_out_norm.bias"))?;
656 let pres_head = DecoderMlp3::load3(m, &format!("{db}presence_token_head."))?;
657 let key = add_rows(enc, pos); let mut inter: Vec<Vec<f32>> = Vec::with_capacity(NL);
659 for l in 0..NL {
660 let b = format!("{db}layers.{l}.");
661 let sine = sine_boxes(&refs);
663 let mut qp = vec![0f32; NQ * FD];
664 rh0.matmat(&sine, NQ, &mut qp, pool);
665 add_bias(&mut qp, FD, &rh0b);
666 qp.iter_mut().for_each(|v| *v = v.max(0.0)); let mut qp2 = vec![0f32; NQ * FD];
668 rh1.matmat(&qp, NQ, &mut qp2, pool);
669 add_bias(&mut qp2, FD, &rh1b);
670 let qp = qp2;
671 let mut qp_full = vec![0f32; (NQ + 1) * FD];
672 qp_full[FD..].copy_from_slice(&qp);
673 let rpb = rpb_bias(&refs, &rx, &ry, n, pool)?;
674 let hqp = add_rows(&hidden, &qp_full);
676 let qkv = lin(m, &format!("{b}self_attn.in_proj_weight"))?;
677 let ib = load_vec(m, &format!("{b}self_attn.in_proj_bias"))?;
678 let op = lin(m, &format!("{b}self_attn.out_proj.weight"))?;
679 let ob = load_vec(m, &format!("{b}self_attn.out_proj.bias"))?;
680 let mut ao = vec![0f32; (NQ + 1) * FD];
681 run_attn(&qkv, &ib, &op, &ob, &hqp, &hqp, &hidden, NQ + 1, NQ + 1, FD, FH, None, Mask::None, pool, &mut ao);
682 for (a, c) in hidden.iter_mut().zip(&ao) { *a += *c; }
683 let (nw, nb) = (load_vec(m, &format!("{b}norm1.weight"))?, load_vec(m, &format!("{b}norm1.bias"))?);
684 layer_norm_rows(&mut hidden, &nw, &nb, D_EPS);
685 let hqp = add_rows(&hidden, &qp_full);
687 let qkv = lin(m, &format!("{b}ca_text.in_proj_weight"))?;
688 let ib = load_vec(m, &format!("{b}ca_text.in_proj_bias"))?;
689 let op = lin(m, &format!("{b}ca_text.out_proj.weight"))?;
690 let ob = load_vec(m, &format!("{b}ca_text.out_proj.bias"))?;
691 let mut ao = vec![0f32; (NQ + 1) * FD];
692 run_attn(&qkv, &ib, &op, &ob, &hqp, text, text, NQ + 1, SS, FD, FH, None, Mask::Key(&km), pool, &mut ao);
693 for (a, c) in hidden.iter_mut().zip(&ao) { *a += *c; }
694 let (nw, nb) = (load_vec(m, &format!("{b}catext_norm.weight"))?, load_vec(m, &format!("{b}catext_norm.bias"))?);
695 layer_norm_rows(&mut hidden, &nw, &nb, D_EPS);
696 let hqp = add_rows(&hidden, &qp_full);
698 let qkv = lin(m, &format!("{b}cross_attn.in_proj_weight"))?;
699 let ib = load_vec(m, &format!("{b}cross_attn.in_proj_bias"))?;
700 let op = lin(m, &format!("{b}cross_attn.out_proj.weight"))?;
701 let ob = load_vec(m, &format!("{b}cross_attn.out_proj.bias"))?;
702 let mut ao = vec![0f32; (NQ + 1) * FD];
703 run_attn(&qkv, &ib, &op, &ob, &hqp, &key, enc, NQ + 1, n, FD, FH, None, Mask::Head(&rpb), pool, &mut ao);
704 for (a, c) in hidden.iter_mut().zip(&ao) { *a += *c; }
705 let (nw, nb) = (load_vec(m, &format!("{b}norm2.weight"))?, load_vec(m, &format!("{b}norm2.bias"))?);
706 layer_norm_rows(&mut hidden, &nw, &nb, D_EPS);
707 let l1 = lin(m, &format!("{b}linear1.weight"))?;
709 let l1b = load_vec(m, &format!("{b}linear1.bias"))?;
710 let l2 = lin(m, &format!("{b}linear2.weight"))?;
711 let l2b = load_vec(m, &format!("{b}linear2.bias"))?;
712 let mut mid = vec![0f32; (NQ + 1) * FI];
713 l1.matmat(&hidden, NQ + 1, &mut mid, pool);
714 add_bias(&mut mid, FI, &l1b);
715 mid.iter_mut().for_each(|v| *v = v.max(0.0));
716 let mut fo = vec![0f32; (NQ + 1) * FD];
717 l2.matmat(&mid, NQ + 1, &mut fo, pool);
718 add_bias(&mut fo, FD, &l2b);
719 for (a, c) in hidden.iter_mut().zip(&fo) { *a += *c; }
720 let (nw, nb) = (load_vec(m, &format!("{b}norm3.weight"))?, load_vec(m, &format!("{b}norm3.bias"))?);
721 layer_norm_rows(&mut hidden, &nw, &nb, D_EPS);
722 let mut qh = hidden[FD..].to_vec();
724 layer_norm_rows(&mut qh, &olnw, &olnb, D_EPS);
725 inter.push(qh.clone());
726 let d = bbox.forward(&qh, NQ, pool);
727 for (r, dd) in refs.iter_mut().zip(d.chunks_exact(4)) {
728 for k in 0..4 { r[k] = sigm(inv_sigmoid(r[k]) + dd[k]); }
729 }
730 }
731 let dec = inter.iter().flat_map(|v| v.iter().copied()).collect();
732 let mut ph = hidden[0..FD].to_vec();
737 layer_norm_rows(&mut ph, &pres_w, &pres_b, D_EPS);
738 let pl = pres_head.forward(&ph, 1, pool);
739 let presence_logit = pl[0].clamp(-10.0, 10.0);
740 Ok((dec, refs, presence_logit))
741 }
742
743 fn score(
752 &self,
753 text: &[f32],
754 am: &[u8],
755 dec: &[f32],
756 pool: Option<&Pool>,
757 ) -> Result<Vec<f32>> {
758 let m = &self.model;
759 let mlp = DecoderMlp::load(m, "det.scoring.prompt_mlp.")?;
760 let mut h = mlp.forward(text, SS, pool, FD);
761 for (a, b) in h.iter_mut().zip(text) {
762 *a += *b;
763 }
764 let (ow, ob) = (
765 load_vec(m, "det.scoring.prompt_mlp.out_norm.weight")?,
766 load_vec(m, "det.scoring.prompt_mlp.out_norm.bias")?,
767 );
768 layer_norm_rows(&mut h, &ow, &ob, D_EPS);
769 let n_valid = am.iter().filter(|&&v| v == 1).count().max(1) as f32;
771 let mut pooled = vec![0f32; FD];
772 for (i, &v) in am.iter().enumerate().take(SS) {
773 if v == 1 {
774 for d in 0..FD {
775 pooled[d] += h[i * FD + d];
776 }
777 }
778 }
779 for v in pooled.iter_mut() {
780 *v /= n_valid;
781 }
782 let pt = lin(m, "det.scoring.prompt_proj.weight")?;
783 let pb = load_vec(m, "det.scoring.prompt_proj.bias")?;
784 let mut tp = vec![0f32; FD];
785 pt.matmat(&pooled, 1, &mut tp, pool);
786 add_bias(&mut tp, FD, &pb);
787 let nq = dec.len() / FD;
788 let hs = lin(m, "det.scoring.hs_proj.weight")?;
789 let hb = load_vec(m, "det.scoring.hs_proj.bias")?;
790 let mut q = vec![0f32; nq * FD];
791 hs.matmat(dec, nq, &mut q, pool);
792 add_bias(&mut q, FD, &hb);
793 let scale = 1.0 / (FD as f32).sqrt();
794 Ok(q
795 .chunks(FD)
796 .map(|r| {
797 let mut acc = 0f32;
798 for d in 0..FD {
799 acc += r[d] * tp[d];
800 }
801 (acc * scale).clamp(-12.0, 12.0)
802 })
803 .collect())
804 }
805
806 #[allow(clippy::too_many_arguments)]
812 fn mask_decoder(
813 &self,
814 queries: &[f32],
815 enc: &[f32],
816 fpn: &[Vec<f32>],
817 text: &[f32],
818 am: &[u8],
819 pool: Option<&Pool>,
820 ) -> Result<Vec<f32>> {
821 let m = &self.model;
822 let db = "det.seghead.";
823 let n = enc.len() / FD;
824 let mut km = vec![0f32; SS];
825 for (k, &v) in am.iter().enumerate() { km[k] = if v == 0 { NEG } else { 0.0 }; }
826 let nw = load_vec(m, &format!("{db}cross_attn_norm.weight"))?;
827 let nb = load_vec(m, &format!("{db}cross_attn_norm.bias"))?;
828 let mut h = enc.to_vec();
829 layer_norm_rows(&mut h, &nw, &nb, D_EPS);
830 let qkv = lin(m, &format!("{db}cross_attend_prompt.in_proj_weight"))?;
831 let ib = load_vec(m, &format!("{db}cross_attend_prompt.in_proj_bias"))?;
832 let op = lin(m, &format!("{db}cross_attend_prompt.out_proj.weight"))?;
833 let ob = load_vec(m, &format!("{db}cross_attend_prompt.out_proj.bias"))?;
834 let mut e = enc.to_vec();
835 let mut ao = vec![0f32; n * FD];
836 run_attn(&qkv, &ib, &op, &ob, &h, text, text, n, SS, FD, FH, None, Mask::Key(&km), pool, &mut ao);
837 for (a, c) in e.iter_mut().zip(&ao) { *a += *c; }
838 let l72 = rows_to_chw(&e, GRID, GRID);
840 let sizes = [(144usize, 144usize), (288, 288)];
841 let mut prev = l72;
842 for (li, &(sh, sw)) in sizes.iter().enumerate() {
843 prev = upsample_nearest(&prev, FD, sh / 2, sh / 2, sh, sw);
844 let fw = &fpn[1 - li]; for (a, b) in prev.iter_mut().zip(fw.iter()) { *a += *b; }
846 let cw = load_vec(m, &format!("{db}pixel_decoder.conv_layers.{li}.weight"))?;
847 let cb = load_vec(m, &format!("{db}pixel_decoder.conv_layers.{li}.bias"))?;
848 prev = conv2d(&cw, &cb, &prev, FD, sh, sw, 3, 3, 1, 1, pool);
849 let gw = load_vec(m, &format!("{db}pixel_decoder.norms.{li}.weight"))?;
850 let gb = load_vec(m, &format!("{db}pixel_decoder.norms.{li}.bias"))?;
851 group_norm(&mut prev, 8, &gw, &gb, 1e-5);
852 prev.iter_mut().for_each(|v| *v = v.max(0.0));
853 }
854 let iw = load_vec(m, &format!("{db}instance_seg_head.weight"))?;
855 let ib2 = load_vec(m, &format!("{db}instance_seg_head.bias"))?;
856 let inst = conv2d(&iw, &ib2, &prev, FD, MH, MH, 1, 1, 1, 0, pool);
857 let mut q = queries.to_vec();
859 for i in 0..3 {
860 let w = lin(m, &format!("{db}mask_predictor.mask_embed.layers.{i}.weight"))?;
861 let b = load_vec(m, &format!("{db}mask_predictor.mask_embed.layers.{i}.bias"))?;
862 let mut o = vec![0f32; NQ * FD];
863 w.matmat(&q, NQ, &mut o, pool);
864 add_bias(&mut o, FD, &b);
865 if i < 2 { o.iter_mut().for_each(|v| *v = v.max(0.0)); }
866 q = o;
867 }
868 let hw = MH * MH;
869 let mut out = vec![0f32; NQ * hw];
870 let ob2 = out.as_mut_ptr() as usize;
871 let run = |s: usize, e: usize| {
872 let o = unsafe { std::slice::from_raw_parts_mut(ob2 as *mut f32, NQ * hw) };
873 for qi in s..e {
874 let qq = &q[qi * FD..(qi + 1) * FD];
875 let dst = &mut o[qi * hw..(qi + 1) * hw];
876 for c in 0..FD {
877 let wgt = qq[c];
878 if wgt == 0.0 { continue; }
879 let ic = &inst[c * hw..(c + 1) * hw];
880 for p in 0..hw { dst[p] += wgt * ic[p]; }
881 }
882 }
883 };
884 match pool { Some(p) => p.run_rows(NQ, &run), None => run(0, NQ) }
885 Ok(out)
886 }
887}
888
889pub(crate) fn fpn_levels(
894 m: &Arc<CmfModel>,
895 p: &str,
896 trunk: &[f32],
897 pool: Option<&Pool>,
898 n_levels: usize,
899) -> Result<Vec<Vec<f32>>> {
900 let hidden = rows_to_chw(trunk, GRID, GRID); let scales = [4.0f32, 2.0, 1.0, 0.5];
902 let mut levels = Vec::with_capacity(n_levels);
903 for (i, &s) in scales.iter().enumerate().take(n_levels) {
904 let (mut ci, mut ch, mut cw) = (TD, GRID, GRID);
905 let mut cur = hidden.clone();
906 match s {
907 4.0 => {
908 let w0 = load_vec(m, &format!("{p}.{i}.dconv_2x2_0.weight"))?;
909 let b0 = load_vec(m, &format!("{p}.{i}.dconv_2x2_0.bias"))?;
910 cur = convt2x2(&w0, &b0, &cur, ci, ch, cw, pool);
911 (ch, cw, ci) = (ch * 2, cw * 2, b0.len());
912 cur.iter_mut().for_each(|v| *v = gelu_erf(*v));
913 let w1 = load_vec(m, &format!("{p}.{i}.dconv_2x2_1.weight"))?;
914 let b1 = load_vec(m, &format!("{p}.{i}.dconv_2x2_1.bias"))?;
915 cur = convt2x2(&w1, &b1, &cur, ci, ch, cw, pool);
916 (ch, cw, ci) = (ch * 2, cw * 2, b1.len());
917 }
918 2.0 => {
919 let w0 = load_vec(m, &format!("{p}.{i}.dconv_2x2.weight"))?;
920 let b0 = load_vec(m, &format!("{p}.{i}.dconv_2x2.bias"))?;
921 cur = convt2x2(&w0, &b0, &cur, ci, ch, cw, pool);
922 (ch, cw, ci) = (ch * 2, cw * 2, b0.len());
923 }
924 1.0 => {}
925 _ => {
926 cur = maxpool2(&cur, ci, ch, cw);
927 (ch, cw) = (ch / 2, cw / 2);
928 }
929 }
930 let w1 = load_vec(m, &format!("{p}.{i}.conv_1x1.weight"))?;
931 let b1 = load_vec(m, &format!("{p}.{i}.conv_1x1.bias"))?;
932 cur = conv2d(&w1, &b1, &cur, ci, ch, cw, 1, 1, 1, 0, pool);
933 ci = b1.len();
934 let w3 = load_vec(m, &format!("{p}.{i}.conv_3x3.weight"))?;
935 let b3 = load_vec(m, &format!("{p}.{i}.conv_3x3.bias"))?;
936 cur = conv2d(&w3, &b3, &cur, ci, ch, cw, 3, 3, 1, 1, pool);
937 levels.push(cur);
938 }
939 Ok(levels)
940}
941
942struct DecoderMlp {
946 w0: QTensor,
947 b0: Vec<f32>,
948 w1: QTensor,
949 b1: Vec<f32>,
950}
951
952impl DecoderMlp {
953 fn load(m: &Arc<CmfModel>, prefix: &str) -> Result<Self> {
954 Ok(Self {
955 w0: lin(m, &format!("{prefix}layers.0.weight"))?,
956 b0: load_vec(m, &format!("{prefix}layers.0.bias"))?,
957 w1: lin(m, &format!("{prefix}layers.1.weight"))?,
958 b1: load_vec(m, &format!("{prefix}layers.1.bias"))?,
959 })
960 }
961 fn forward(&self, x: &[f32], rows: usize, pool: Option<&Pool>, out_w: usize) -> Vec<f32> {
962 let mut h = vec![0f32; rows * self.b0.len()];
963 self.w0.matmat(x, rows, &mut h, pool);
964 add_bias(&mut h, self.b0.len(), &self.b0);
965 h.iter_mut().for_each(|v| *v = v.max(0.0));
966 let mut o = vec![0f32; rows * out_w];
967 self.w1.matmat(&h, rows, &mut o, pool);
968 add_bias(&mut o, out_w, &self.b1);
969 o
970 }
971}
972
973struct DecoderMlp3 {
975 w0: QTensor,
976 b0: Vec<f32>,
977 w1: QTensor,
978 b1: Vec<f32>,
979 w2: QTensor,
980 b2: Vec<f32>,
981}
982
983impl DecoderMlp3 {
984 fn load3(m: &Arc<CmfModel>, prefix: &str) -> Result<Self> {
985 Ok(Self {
986 w0: lin(m, &format!("{prefix}layers.0.weight"))?,
987 b0: load_vec(m, &format!("{prefix}layers.0.bias"))?,
988 w1: lin(m, &format!("{prefix}layers.1.weight"))?,
989 b1: load_vec(m, &format!("{prefix}layers.1.bias"))?,
990 w2: lin(m, &format!("{prefix}layers.2.weight"))?,
991 b2: load_vec(m, &format!("{prefix}layers.2.bias"))?,
992 })
993 }
994 fn forward(&self, x: &[f32], rows: usize, pool: Option<&Pool>) -> Vec<f32> {
995 let mut d = vec![0f32; rows * self.b0.len()];
996 self.w0.matmat(x, rows, &mut d, pool);
997 add_bias(&mut d, self.b0.len(), &self.b0);
998 d.iter_mut().for_each(|v| *v = v.max(0.0));
999 let mut d2 = vec![0f32; rows * self.b1.len()];
1000 self.w1.matmat(&d, rows, &mut d2, pool);
1001 add_bias(&mut d2, self.b1.len(), &self.b1);
1002 d2.iter_mut().for_each(|v| *v = v.max(0.0));
1003 let mut o = vec![0f32; rows * self.b2.len()];
1004 self.w2.matmat(&d2, rows, &mut o, pool);
1005 add_bias(&mut o, self.b2.len(), &self.b2);
1006 o
1007 }
1008}
1009
1010pub(crate) fn sigm(x: f32) -> f32 {
1011 1.0 / (1.0 + (-x).exp())
1012}
1013
1014fn inv_sigmoid(x: f32) -> f32 {
1015 let x = x.clamp(0.0, 1.0);
1016 ((x.max(1e-3)) / ((1.0 - x).max(1e-3))).ln()
1017}
1018
1019fn xyxy(boxes: &[[f32; 4]]) -> Vec<f32> {
1020 let mut out = vec![0f32; boxes.len() * 4];
1021 for (i, b) in boxes.iter().enumerate() {
1022 out[i * 4] = b[0] - 0.5 * b[2];
1023 out[i * 4 + 1] = b[1] - 0.5 * b[3];
1024 out[i * 4 + 2] = b[0] + 0.5 * b[2];
1025 out[i * 4 + 3] = b[1] + 0.5 * b[3];
1026 }
1027 out
1028}
1029
1030pub(crate) fn lin(m: &Arc<CmfModel>, name: &str) -> Result<QTensor> {
1031 QTensor::from_model(m, name).map_err(|e| anyhow!(e))
1032}
1033
1034pub(crate) fn load_vec(m: &Arc<CmfModel>, name: &str) -> Result<Vec<f32>> {
1036 let e = m.tensor(name).ok_or_else(|| anyhow!("tensor '{name}' not found"))?;
1037 if matches!(e.dtype, TensorDtype::U8) { return Err(anyhow!("'{name}' is u8")); }
1038 let bytes = m.entry_bytes(e);
1039 let mut dst = vec![0f32; e.n_elems()];
1040 dequant_tensor(e, bytes, &mut dst).map_err(|er| anyhow!("{name}: {er}"))?;
1041 Ok(dst)
1042}
1043
1044pub(crate) fn add_bias(out: &mut [f32], cols: usize, bias: &[f32]) {
1046 debug_assert_eq!(bias.len(), cols);
1047 for r in out.chunks_exact_mut(cols) {
1048 for (v, b) in r.iter_mut().zip(bias) { *v += *b; }
1049 }
1050}
1051
1052pub(crate) fn layer_norm_rows(x: &mut [f32], w: &[f32], b: &[f32], eps: f32) {
1054 let d = w.len();
1055 for r in x.chunks_exact_mut(d) {
1056 let mean = r.iter().sum::<f32>() / d as f32;
1057 let var = r.iter().map(|v| (v - mean) * (v - mean)).sum::<f32>() / d as f32;
1058 let inv = 1.0 / (var + eps).sqrt();
1059 for (v, (&ww, &bb)) in r.iter_mut().zip(w.iter().zip(b.iter())) {
1060 *v = (*v - mean) * inv * ww + bb;
1061 }
1062 }
1063}
1064
1065fn apply_rope(buf: &mut [f32], n: usize, stride: usize, width: usize, fc: &[f32]) {
1071 let npairs = 32;
1072 debug_assert_eq!(fc.len(), n * npairs * 2);
1073 let blocks = width / 64;
1074 for i in 0..n {
1075 let f = &fc[i * npairs * 2..(i + 1) * npairs * 2];
1076 let row = &mut buf[i * stride..i * stride + width];
1077 for b in 0..blocks {
1078 let base = b * 64;
1079 for j in 0..npairs {
1080 let (c, s) = (f[j * 2], f[j * 2 + 1]);
1081 let (a, d) = (row[base + j * 2], row[base + j * 2 + 1]);
1082 row[base + j * 2] = a * c - d * s;
1083 row[base + j * 2 + 1] = d * c + a * s;
1084 }
1085 }
1086 }
1087}
1088
1089#[allow(clippy::too_many_arguments)]
1094pub(crate) fn run_attn(
1095 w_qkv: &QTensor,
1096 qb: &[f32],
1097 w_o: &QTensor,
1098 ob: &[f32],
1099 xs_q: &[f32],
1100 xs_k: &[f32],
1101 xs_v: &[f32],
1102 n_q: usize,
1103 n_k: usize,
1104 dim: usize,
1105 heads: usize,
1106 rope: Option<&[f32]>,
1107 mask: Mask,
1108 pool: Option<&Pool>,
1109 out: &mut [f32],
1110) {
1111 let h3 = dim * 3;
1112 let hd = dim / heads;
1113 let scale = 1.0 / (hd as f32).sqrt();
1114 let mut qkv_q = vec![0f32; n_q * h3];
1115 w_qkv.matmat(xs_q, n_q, &mut qkv_q, pool);
1116 add_bias(&mut qkv_q, h3, qb);
1117 let same_k = xs_k.as_ptr() == xs_q.as_ptr();
1118 let mut kbuf = Vec::new();
1119 if !same_k {
1120 kbuf = vec![0f32; n_k * h3];
1121 w_qkv.matmat(xs_k, n_k, &mut kbuf, pool);
1122 add_bias(&mut kbuf, h3, qb);
1123 }
1124 let same_v = xs_v.as_ptr() == xs_q.as_ptr();
1125 let mut vbuf = Vec::new();
1126 if !same_v {
1127 vbuf = vec![0f32; n_k * h3];
1128 w_qkv.matmat(xs_v, n_k, &mut vbuf, pool);
1129 add_bias(&mut vbuf, h3, qb);
1130 }
1131 if let Some(fc) = rope {
1132 apply_rope(&mut qkv_q, n_q, h3, dim * 2, fc);
1134 if !same_k {
1135 apply_rope(&mut kbuf, n_k, h3, dim * 2, fc);
1136 }
1137 }
1138 let qkv_k: &[f32] = if same_k { &qkv_q } else { &kbuf };
1139 let qkv_v: &[f32] = if same_v { &qkv_q } else { &vbuf };
1140 let mut attn = vec![0f32; n_q * dim];
1141 let ab = attn.as_mut_ptr() as usize;
1142 let run = |s: usize, e: usize| {
1143 let a = unsafe { std::slice::from_raw_parts_mut(ab as *mut f32, n_q * dim) };
1144 let mut scores = vec![0f32; n_k];
1145 for i in s..e {
1146 let qrow = &qkv_q[i * h3..i * h3 + dim];
1147 for t in 0..heads {
1148 let qs = &qrow[t * hd..(t + 1) * hd];
1149 let mut mx = f32::NEG_INFINITY;
1150 for j in 0..n_k {
1151 let kr = &qkv_k[j * h3 + dim + t * hd..j * h3 + dim + (t + 1) * hd];
1152 let mut v = qs.iter().zip(kr).fold(0.0f32, |c, (a, b)| c + a * b) * scale;
1153 v += match mask {
1154 Mask::None => 0.0,
1155 Mask::Key(km) => km[j],
1156 Mask::Add(am) => am[i * n_k + j],
1157 Mask::Head(hm) => hm[(t * n_q + i) * n_k + j],
1158 };
1159 scores[j] = v;
1160 if v > mx { mx = v; }
1161 }
1162 let mut sum = 0.0f32;
1167 for sv in scores.iter_mut().take(n_k) {
1168 *sv = (*sv - mx).exp();
1169 sum += *sv;
1170 }
1171 let inv = 1.0 / sum;
1172 let orow = &mut a[i * dim + t * hd..i * dim + (t + 1) * hd];
1173 for j in 0..n_k {
1174 let wgt = scores[j] * inv;
1175 if wgt == 0.0 { continue; }
1176 let vr = &qkv_v[j * h3 + 2 * dim + t * hd..j * h3 + 2 * dim + (t + 1) * hd];
1177 for (o, vv) in orow.iter_mut().zip(vr) { *o += wgt * vv; }
1178 }
1179 }
1180 }
1181 };
1182 match pool { Some(p) => p.run_rows(n_q, &run), None => run(0, n_q) }
1183 w_o.matmat(&attn, n_q, out, pool);
1184 add_bias(out, dim, ob);
1185}
1186
1187pub(crate) fn conv2d(
1190 w: &[f32],
1191 b: &[f32],
1192 x: &[f32],
1193 ci: usize,
1194 h: usize,
1195 wid: usize,
1196 kh: usize,
1197 kw: usize,
1198 stride: usize,
1199 pad: usize,
1200 pool: Option<&Pool>,
1201) -> Vec<f32> {
1202 let co = b.len();
1203 debug_assert_eq!(w.len(), co * ci * kh * kw);
1204 debug_assert_eq!(x.len(), ci * h * wid);
1205 let oh = (h + 2 * pad - kh) / stride + 1;
1206 let ow = (wid + 2 * pad - kw) / stride + 1;
1207 let mut out = vec![0f32; co * oh * ow];
1208 let ob = out.as_mut_ptr() as usize;
1209 let run = |s: usize, e: usize| {
1210 let o = unsafe { std::slice::from_raw_parts_mut(ob as *mut f32, co * oh * ow) };
1211 for idx in s..e {
1212 let c_o = idx / (oh * ow);
1213 let rem = idx % (oh * ow);
1214 let (oy, ox) = (rem / ow, rem % ow);
1215 let mut acc = b[c_o];
1216 for c in 0..ci {
1217 for dy in 0..kh {
1218 let iy = oy * stride + dy;
1219 if iy < pad || iy - pad >= h { continue; }
1220 for dx in 0..kw {
1221 let ix = ox * stride + dx;
1222 if ix < pad || ix - pad >= wid { continue; }
1223 acc += w[((c_o * ci + c) * kh + dy) * kw + dx]
1224 * x[(c * h + iy - pad) * wid + (ix - pad)];
1225 }
1226 }
1227 }
1228 o[idx] = acc;
1229 }
1230 };
1231 match pool { Some(p) => p.run_rows(co * oh * ow, &run), None => run(0, co * oh * ow) }
1232 out
1233}
1234
1235pub(crate) fn convt2x2(w: &[f32], b: &[f32], x: &[f32], ci: usize, h: usize, wid: usize, pool: Option<&Pool>) -> Vec<f32> {
1237 let co = b.len();
1238 debug_assert_eq!(w.len(), ci * co * 4);
1239 let (oh, ow) = (h * 2, wid * 2);
1240 let mut out = vec![0f32; co * oh * ow];
1241 let ob = out.as_mut_ptr() as usize;
1242 let run = |s: usize, e: usize| {
1243 let o = unsafe { std::slice::from_raw_parts_mut(ob as *mut f32, co * oh * ow) };
1244 for idx in s..e {
1245 let c_o = idx / (oh * ow);
1246 let rem = idx % (oh * ow);
1247 let (oy, ox) = (rem / ow, rem % ow);
1248 let (kh, kx) = (oy & 1, ox & 1);
1249 let (iy, ix) = (oy >> 1, ox >> 1);
1250 let mut acc = b[c_o];
1251 for c in 0..ci {
1252 acc += w[((c * co + c_o) * 2 + kh) * 2 + kx] * x[(c * h + iy) * wid + ix];
1253 }
1254 o[idx] = acc;
1255 }
1256 };
1257 match pool { Some(p) => p.run_rows(co * oh * ow, &run), None => run(0, co * oh * ow) }
1258 out
1259}
1260
1261pub(crate) fn maxpool2(x: &[f32], ci: usize, h: usize, wid: usize) -> Vec<f32> {
1262 let (oh, ow) = (h / 2, wid / 2);
1263 let mut out = vec![0f32; ci * oh * ow];
1264 for c in 0..ci {
1265 for oy in 0..oh {
1266 for ox in 0..ow {
1267 let mut mx = f32::NEG_INFINITY;
1268 for dy in 0..2 {
1269 for dx in 0..2 {
1270 let v = x[(c * h + oy * 2 + dy) * wid + ox * 2 + dx];
1271 if v > mx { mx = v; }
1272 }
1273 }
1274 out[(c * oh + oy) * ow + ox] = mx;
1275 }
1276 }
1277 }
1278 out
1279}
1280
1281pub(crate) fn upsample_nearest(x: &[f32], ci: usize, h: usize, wid: usize, sh: usize, sw: usize) -> Vec<f32> {
1283 let mut out = vec![0f32; ci * sh * sw];
1284 for c in 0..ci {
1285 for oy in 0..sh {
1286 let iy = oy * h / sh;
1287 for ox in 0..sw {
1288 let ix = ox * wid / sw;
1289 out[(c * sh + oy) * sw + ox] = x[(c * h + iy) * wid + ix];
1290 }
1291 }
1292 }
1293 out
1294}
1295
1296pub(crate) fn group_norm(x: &mut [f32], groups: usize, w: &[f32], b: &[f32], eps: f32) {
1298 let co = w.len();
1299 let gsz = co / groups;
1300 let rest = x.len() / co;
1301 for g in 0..groups {
1302 let s = g * gsz * rest;
1303 let e = s + gsz * rest;
1304 let mean = x[s..e].iter().sum::<f32>() / (e - s) as f32;
1305 let var = x[s..e].iter().map(|v| (v - mean) * (v - mean)).sum::<f32>() / (e - s) as f32;
1306 let inv = 1.0 / (var + eps).sqrt();
1307 for (i, v) in x[s..e].iter_mut().enumerate() {
1308 let ch = g * gsz + (i / rest);
1309 *v = (*v - mean) * inv * w[ch] + b[ch];
1310 }
1311 }
1312}
1313
1314pub(crate) fn rows_to_chw(rows: &[f32], h: usize, w: usize) -> Vec<f32> {
1317 let n = h * w;
1318 let c = rows.len() / n;
1319 let mut out = vec![0f32; c * h * w];
1320 for ch in 0..c {
1321 for y in 0..h {
1322 for x in 0..w { out[(ch * h + y) * w + x] = rows[(y * w + x) * c + ch]; }
1323 }
1324 }
1325 out
1326}
1327
1328pub(crate) fn chw_to_rows(chw: &[f32], h: usize, w: usize) -> Vec<f32> {
1329 let c = chw.len() / (h * w);
1330 let mut out = vec![0f32; h * w * c];
1331 for ch in 0..c {
1332 for y in 0..h {
1333 for x in 0..w { out[(y * w + x) * c + ch] = chw[(ch * h + y) * w + x]; }
1334 }
1335 }
1336 out
1337}
1338
1339pub(crate) fn sine_pos(h: usize, w: usize, n: usize) -> Vec<f32> {
1342 let scale = std::f32::consts::TAU;
1343 let mut dim_t = vec![0f32; n];
1344 for i in 0..n { dim_t[i] = 10000f32.powf((2 * (i / 2)) as f32 / n as f32); }
1345 let mut out = vec![0f32; h * w * n * 2];
1346 let (hn, wn) = (h as f32, w as f32);
1347 for y in 0..h {
1348 let yy = (y + 1) as f32 / (hn + 1e-6) * scale;
1349 for x in 0..w {
1350 let xx = (x + 1) as f32 / (wn + 1e-6) * scale;
1351 let row = (y * w + x) * n * 2;
1352 for i in 0..n {
1353 let (py, px) = (yy / dim_t[i], xx / dim_t[i]);
1354 if i % 2 == 0 {
1355 out[row + i] = py.sin();
1356 out[row + n + i] = px.sin();
1357 } else {
1358 out[row + i] = py.cos();
1359 out[row + n + i] = px.cos();
1360 }
1361 }
1362 }
1363 }
1364 out
1365}
1366
1367fn sine_boxes(boxes: &[[f32; 4]]) -> Vec<f32> {
1371 let nf = 128;
1372 let scale = std::f32::consts::TAU;
1373 let mut dim_t = vec![0f32; nf];
1374 for i in 0..nf { dim_t[i] = 10000f32.powf((2 * (i / 2)) as f32 / nf as f32); }
1375 let mut out = vec![0f32; boxes.len() * 4 * nf];
1376 for (q, b) in boxes.iter().enumerate() {
1377 let base = q * 4 * nf;
1378 for (ci, v) in [b[1], b[0], b[2], b[3]].iter().enumerate() {
1379 for i in 0..nf {
1380 let val = v * scale / dim_t[i];
1381 out[base + ci * nf + i] = if i % 2 == 0 { val.sin() } else { val.cos() };
1382 }
1383 }
1384 }
1385 out
1386}
1387
1388fn rpb_bias(refs: &[[f32; 4]], rx: &DecoderMlp, ry: &DecoderMlp, n: usize, pool: Option<&Pool>) -> Result<Vec<f32>> {
1391 let gh = GRID;
1392 debug_assert_eq!(n, gh * gh);
1393 let l2_8 = 8.0f32.ln();
1394 let mut dy = vec![0f32; refs.len() * gh * 2];
1395 let mut dx = vec![0f32; refs.len() * gh * 2];
1396 for (q, r) in refs.iter().enumerate() {
1397 let x1 = r[0] - 0.5 * r[2];
1398 let y1 = r[1] - 0.5 * r[3];
1399 let x2 = r[0] + 0.5 * r[2];
1400 let y2 = r[1] + 0.5 * r[3];
1401 for hi in 0..gh {
1402 let c = hi as f32 / gh as f32;
1403 dy[(q * gh + hi) * 2] = log_compress(c - y1, l2_8);
1404 dy[(q * gh + hi) * 2 + 1] = log_compress(c - y2, l2_8);
1405 }
1406 for wi in 0..gh {
1407 let c = wi as f32 / gh as f32;
1408 dx[(q * gh + wi) * 2] = log_compress(c - x1, l2_8);
1409 dx[(q * gh + wi) * 2 + 1] = log_compress(c - x2, l2_8);
1410 }
1411 }
1412 let py = ry.forward(&dy, refs.len() * gh, pool, FH);
1413 let px = rx.forward(&dx, refs.len() * gh, pool, FH);
1414 let mut out = vec![0f32; FH * (NQ + 1) * n];
1415 for t in 0..FH {
1416 for q in 0..refs.len() {
1417 for hi in 0..gh {
1418 for wi in 0..gh {
1419 out[(t * (NQ + 1) + (q + 1)) * n + hi * gh + wi] =
1420 py[(q * gh + hi) * FH + t] + px[(q * gh + wi) * FH + t];
1421 }
1422 }
1423 }
1424 }
1425 Ok(out)
1426}
1427
1428fn log_compress(d: f32, l2_8: f32) -> f32 {
1430 d.signum() * ((8.0 * d).abs() + 1.0).log2() / l2_8
1431}
1432
1433fn add_rows(a: &[f32], b: &[f32]) -> Vec<f32> {
1435 a.iter().zip(b).map(|(x, y)| x + y).collect()
1436}
1437
1438#[allow(dead_code)]
1440fn dbg_stats(tag: &str, v: &[f32]) {
1441 if std::env::var("SAM3_TEXT_DEBUG").is_err() {
1442 return;
1443 }
1444 let (mut bad, mut mn, mut mx) = (0usize, f32::INFINITY, f32::NEG_INFINITY);
1445 for &x in v {
1446 if !x.is_finite() {
1447 bad += 1;
1448 continue;
1449 }
1450 if x < mn { mn = x; }
1451 if x > mx { mx = x; }
1452 }
1453 eprintln!(
1454 "[sam3] {tag} n={} nonfinite={} min={:.4e} max={:.4e}",
1455 v.len(), bad, mn, mx
1456 );
1457}
1458
1459#[allow(dead_code)]
1461fn dump_f32(dir: &str, name: &str, v: &[f32]) {
1462 use std::io::Write;
1463 let p = std::path::Path::new(dir).join(format!("{name}.f32"));
1464 let mut b = Vec::with_capacity(v.len() * 4);
1465 for x in v {
1466 b.extend_from_slice(&x.to_le_bytes());
1467 }
1468 match std::fs::File::create(&p).and_then(|mut f| f.write_all(&b)) {
1469 Ok(()) => eprintln!("[sam3] dumped {} ({} elems)", p.display(), v.len()),
1470 Err(e) => eprintln!("[sam3] dump {} failed: {e}", p.display()),
1471 }
1472}