1use crate::pool::Pool;
19use crate::qtensor::QTensor;
20use crate::vae::{StTensor, read_safetensors};
21use cortiq_core::CmfModel;
22use std::collections::HashMap;
23use std::path::Path;
24use std::sync::Arc;
25
26pub(crate) enum Proj {
29 F32 {
30 w: Vec<f32>,
31 rows: usize,
32 cols: usize,
33 },
34 Q(QTensor),
35}
36
37impl Proj {
38 pub(crate) fn f32(w: Vec<f32>, cols: usize) -> Self {
40 let rows = w.len() / cols;
41 debug_assert_eq!(w.len(), rows * cols);
42 Proj::F32 { w, rows, cols }
43 }
44
45 pub(crate) fn from_model(model: &Arc<CmfModel>, name: &str) -> Result<Self, String> {
48 Ok(match QTensor::from_model(model, name)? {
49 QTensor::F32 { data, rows, cols } => Proj::F32 {
50 w: data,
51 rows,
52 cols,
53 },
54 q => Proj::Q(q),
55 })
56 }
57
58 pub(crate) fn rows(&self) -> usize {
59 match self {
60 Proj::F32 { rows, .. } => *rows,
61 Proj::Q(q) => q.rows(),
62 }
63 }
64
65 pub(crate) fn graph_w(&self) -> Option<(&Arc<CmfModel>, crate::gpu::GraphW<'_>)> {
68 match self {
69 Proj::Q(q) => q.graph_weight().map(|(m, idx, kind, rs)| {
70 (
71 m,
72 crate::gpu::GraphW {
73 idx,
74 kind,
75 row_scale: rs,
76 data: &[],
77 },
78 )
79 }),
80 Proj::F32 { .. } => None,
81 }
82 }
83
84 pub(crate) fn cols(&self) -> usize {
85 match self {
86 Proj::F32 { cols, .. } => *cols,
87 Proj::Q(q) => q.cols(),
88 }
89 }
90
91 pub(crate) fn matvec_rows(&self, xs: &[f32], b: usize, out: &mut [f32], pool: Option<&Pool>) {
100 let (rows, cols) = (self.rows(), self.cols());
101 match self {
102 Proj::F32 { w, .. } => crate::fcd_ops::gemm_nt(xs, w, out, b, cols, rows, pool),
103 Proj::Q(q) => {
104 for i in 0..b {
105 q.matvec(
106 &xs[i * cols..(i + 1) * cols],
107 &mut out[i * rows..(i + 1) * rows],
108 pool,
109 );
110 }
111 }
112 }
113 }
114
115 pub(crate) fn matmat(&self, xs: &[f32], b: usize, out: &mut [f32], pool: Option<&Pool>) {
117 match self {
118 Proj::F32 { w, rows, cols } => {
119 crate::fcd_ops::gemm_nt(xs, w, out, b, *cols, *rows, pool)
120 }
121 Proj::Q(q) => q.matmat(xs, b, out, pool),
122 }
123 }
124}
125
126struct Block {
127 modulation: Option<(Proj, Vec<f32>)>,
129 norm1: Vec<f32>,
130 q: Proj, k: Proj, v: Proj,
133 o: Proj, norm_q: Vec<f32>, norm_k: Vec<f32>,
136 norm2: Vec<f32>,
137 ffn_norm1: Vec<f32>,
138 w1: Proj, w3: Proj, w2: Proj, ffn_norm2: Vec<f32>,
142}
143
144pub struct NextDit {
147 x_emb: Proj, x_emb_b: Vec<f32>,
149 t_lin1_w: Vec<f32>, t_lin1_b: Vec<f32>,
151 t_lin2_w: Vec<f32>, t_lin2_b: Vec<f32>,
153 cap_norm: Vec<f32>, cap_w: Proj, cap_b: Vec<f32>,
156 context_refiner: Vec<Block>,
157 noise_refiner: Vec<Block>,
158 layers: Vec<Block>,
159 out_lin1_w: Vec<f32>, out_lin1_b: Vec<f32>,
161 out_lin2: Proj, out_lin2_b: Vec<f32>,
163 pool: Option<Arc<Pool>>,
164 pub hidden: usize,
165 pub in_channels: usize,
166 pub patch: usize,
167 nh: usize,
168 nkv: usize,
169 hd: usize,
170 axes_dim: Vec<usize>,
171 eps: f64,
172}
173
174mod prof {
179 use std::sync::OnceLock;
180 use std::sync::atomic::{AtomicU64, Ordering};
181
182 pub const MODNORM: usize = 0;
183 pub const QKV: usize = 1;
184 pub const ROPE: usize = 2;
185 pub const APACK: usize = 3;
186 pub const AQK: usize = 4;
187 pub const SOFTMAX: usize = 5;
188 pub const APV: usize = 6;
189 pub const OPROJ: usize = 7;
190 pub const FFN: usize = 8;
191 pub const FFNEL: usize = 9;
192 pub const HEADTAIL: usize = 10;
193 pub const GPUBLK: usize = 11;
194 const NAMES: [&str; 12] = [
195 "mod+norms",
196 "qkv-proj",
197 "qknorm+rope",
198 "attn-pack",
199 "attn-qk",
200 "softmax",
201 "attn-pv",
202 "o-proj",
203 "ffn-mm",
204 "ffn-silu",
205 "head+tail",
206 "gpu-block",
207 ];
208 static NS: [AtomicU64; 12] = [const { AtomicU64::new(0) }; 12];
209
210 pub fn on() -> bool {
211 static ON: OnceLock<bool> = OnceLock::new();
212 *ON.get_or_init(|| std::env::var("CMF_DIT_PROF").is_ok_and(|v| v != "0"))
213 }
214
215 pub struct Span(Option<(std::time::Instant, usize)>);
217 pub fn span(cat: usize) -> Span {
218 Span(on().then(|| (std::time::Instant::now(), cat)))
219 }
220 impl Drop for Span {
221 fn drop(&mut self) {
222 if let Some((t0, c)) = self.0 {
223 NS[c].fetch_add(t0.elapsed().as_nanos() as u64, Ordering::Relaxed);
224 }
225 }
226 }
227
228 pub fn dump() {
229 if !on() {
230 return;
231 }
232 let total: u64 = NS.iter().map(|a| a.load(Ordering::Relaxed)).sum();
233 if total == 0 {
234 return;
235 }
236 eprintln!("dit prof ({:.1} s total in blocks):", total as f64 / 1e9);
237 for (name, a) in NAMES.iter().zip(&NS) {
238 let ns = a.load(Ordering::Relaxed);
239 eprintln!(
240 " {name:<12} {:>7.2} s {:>4.1}%",
241 ns as f64 / 1e9,
242 ns as f64 * 100.0 / total as f64
243 );
244 }
245 }
246}
247
248fn rms_norm(x: &[f32], w: &[f32], eps: f64) -> Vec<f32> {
250 let ss = x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / x.len() as f64;
251 let inv = 1.0 / (ss + eps).sqrt();
252 x.iter()
253 .zip(w)
254 .map(|(&v, &g)| (v as f64 * inv) as f32 * g)
255 .collect()
256}
257
258fn rms_norm_into(x: &[f32], w: &[f32], eps: f64, dst: &mut [f32]) {
260 let ss = x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / x.len() as f64;
261 let inv = 1.0 / (ss + eps).sqrt();
262 for ((d, &v), &g) in dst.iter_mut().zip(x).zip(w) {
263 *d = (v as f64 * inv) as f32 * g;
264 }
265}
266
267fn rms_norm_inplace(v: &mut [f32], w: &[f32], eps: f64) {
269 let ss = v.iter().map(|&x| (x as f64) * (x as f64)).sum::<f64>() / v.len() as f64;
270 let inv = 1.0 / (ss + eps).sqrt();
271 for (x, &g) in v.iter_mut().zip(w) {
272 *x = (*x as f64 * inv) as f32 * g;
273 }
274}
275
276fn pool_rows(pool: Option<&Pool>, n: usize, f: &(dyn Fn(usize, usize) + Sync)) {
278 match pool {
279 Some(p) => p.run_rows(n, f),
280 None => f(0, n),
281 }
282}
283
284fn silu(v: f32) -> f32 {
285 v / (1.0 + (-v).exp())
286}
287
288fn linear(x: &[f32], w: &[f32], b: &[f32]) -> Vec<f32> {
290 let k = x.len();
291 b.iter()
292 .enumerate()
293 .map(|(o, &bias)| {
294 let row = &w[o * k..(o + 1) * k];
295 bias + row.iter().zip(x).map(|(&a, &c)| a * c).sum::<f32>()
296 })
297 .collect()
298}
299
300struct SendRows(*mut f32);
304unsafe impl Send for SendRows {}
305unsafe impl Sync for SendRows {}
306impl SendRows {
307 #[allow(clippy::mut_from_ref)] unsafe fn row(&self, off: usize, len: usize) -> &mut [f32] {
310 unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
311 }
312
313 unsafe fn set(&self, off: usize, v: f32) {
315 unsafe { *self.0.add(off) = v }
316 }
317}
318
319fn softmax_inplace(row: &mut [f32]) {
322 #[cfg(target_arch = "aarch64")]
323 {
324 crate::attention::softmax_row(row);
325 }
326 #[cfg(not(target_arch = "aarch64"))]
327 {
328 let mx = row.iter().cloned().fold(f32::MIN, f32::max);
329 let mut den = 0f32;
330 for r in row.iter_mut() {
331 *r = (*r - mx).exp();
332 den += *r;
333 }
334 if den > 0.0 {
335 let inv = 1.0 / den;
336 for r in row.iter_mut() {
337 *r *= inv;
338 }
339 }
340 }
341}
342
343pub(crate) fn cmf_f32(model: &CmfModel, name: &str) -> Result<Vec<f32>, String> {
345 let entry = model
346 .tensor(name)
347 .ok_or_else(|| format!("missing tensor {name}"))?;
348 let bytes = model.entry_bytes(entry);
349 let mut out = vec![0f32; entry.shape.iter().product()];
350 cortiq_core::quant::dequant_tensor(entry, bytes, &mut out)?;
351 Ok(out)
352}
353
354fn rope_table(ids: &[[u32; 3]], axes_dim: &[usize]) -> (Vec<f64>, Vec<f64>) {
357 let pairs: usize = axes_dim.iter().sum::<usize>() / 2;
358 let mut cos = Vec::with_capacity(ids.len() * pairs);
359 let mut sin = Vec::with_capacity(ids.len() * pairs);
360 for id in ids {
361 for (a, &d) in axes_dim.iter().enumerate() {
362 for j in 0..d / 2 {
363 let freq = 1.0 / 10000f64.powf(2.0 * j as f64 / d as f64);
364 let ang = id[a] as f64 * freq;
365 cos.push(ang.cos());
366 sin.push(ang.sin());
367 }
368 }
369 }
370 (cos, sin)
371}
372
373impl Drop for NextDit {
374 fn drop(&mut self) {
375 prof::dump();
376 }
377}
378
379impl NextDit {
380 pub fn load_dir(dir: &Path) -> Result<Self, String> {
381 let cfg: serde_json::Value = serde_json::from_slice(
382 &std::fs::read(dir.join("config.json")).map_err(|e| format!("config.json: {e}"))?,
383 )
384 .map_err(|e| format!("config.json: {e}"))?;
385 let idx: serde_json::Value = serde_json::from_slice(
386 &std::fs::read(dir.join("diffusion_pytorch_model.safetensors.index.json"))
387 .map_err(|e| format!("index: {e}"))?,
388 )
389 .map_err(|e| format!("index: {e}"))?;
390 let mut shards: Vec<String> = idx["weight_map"]
391 .as_object()
392 .ok_or("weight_map")?
393 .values()
394 .filter_map(|v| v.as_str().map(String::from))
395 .collect();
396 shards.sort();
397 shards.dedup();
398 let mut t: HashMap<String, StTensor> = HashMap::new();
399 for sh in &shards {
400 t.extend(read_safetensors(&dir.join(sh))?);
401 }
402 let mut take = |n: String| -> Result<Vec<f32>, String> {
403 t.remove(&n)
404 .map(|v| v.data)
405 .ok_or_else(|| format!("missing tensor {n}"))
406 };
407 let hidden = cfg["hidden_size"].as_u64().ok_or("hidden")? as usize;
408 let mut blocks = |pfx: &str, count: usize, modulated: bool| -> Result<Vec<Block>, String> {
409 (0..count)
410 .map(|l| {
411 let p = format!("{pfx}.{l}");
412 let w1 = take(format!("{p}.feed_forward.linear_1.weight"))?;
413 let inter = w1.len() / hidden;
414 Ok(Block {
415 modulation: if modulated {
416 let mw = take(format!("{p}.norm1.linear.weight"))?;
417 let cols = mw.len() / (4 * hidden);
418 Some((Proj::f32(mw, cols), take(format!("{p}.norm1.linear.bias"))?))
419 } else {
420 None
421 },
422 norm1: if modulated {
423 take(format!("{p}.norm1.norm.weight"))?
424 } else {
425 take(format!("{p}.norm1.weight"))?
426 },
427 q: Proj::f32(take(format!("{p}.attn.to_q.weight"))?, hidden),
428 k: Proj::f32(take(format!("{p}.attn.to_k.weight"))?, hidden),
429 v: Proj::f32(take(format!("{p}.attn.to_v.weight"))?, hidden),
430 o: {
431 let o = take(format!("{p}.attn.to_out.0.weight"))?;
432 let cols = o.len() / hidden;
433 Proj::f32(o, cols)
434 },
435 norm_q: take(format!("{p}.attn.norm_q.weight"))?,
436 norm_k: take(format!("{p}.attn.norm_k.weight"))?,
437 norm2: take(format!("{p}.norm2.weight"))?,
438 ffn_norm1: take(format!("{p}.ffn_norm1.weight"))?,
439 w1: Proj::f32(w1, hidden),
440 w3: Proj::f32(take(format!("{p}.feed_forward.linear_3.weight"))?, hidden),
441 w2: Proj::f32(take(format!("{p}.feed_forward.linear_2.weight"))?, inter),
442 ffn_norm2: take(format!("{p}.ffn_norm2.weight"))?,
443 })
444 })
445 .collect()
446 };
447 let nl = cfg["num_layers"].as_u64().ok_or("num_layers")? as usize;
448 let nr = cfg["num_refiner_layers"].as_u64().unwrap_or(2) as usize;
449 let context_refiner = blocks("context_refiner", nr, false)?;
450 let noise_refiner = blocks("noise_refiner", nr, true)?;
451 let layers = blocks("layers", nl, true)?;
452 let nh = cfg["num_attention_heads"].as_u64().ok_or("nh")? as usize;
453 let in_channels = cfg["in_channels"].as_u64().ok_or("in_channels")? as usize;
454 let patch = cfg["patch_size"].as_u64().unwrap_or(2) as usize;
455 let axes_dim: Vec<usize> = cfg["axes_dim_rope"]
456 .as_array()
457 .ok_or("axes_dim_rope")?
458 .iter()
459 .map(|v| v.as_u64().unwrap_or(0) as usize)
460 .collect();
461 let cap_norm = take("time_caption_embed.caption_embedder.0.weight".into())?;
462 let cap_feat = cap_norm.len();
463 Ok(Self {
464 x_emb: Proj::f32(
465 take("x_embedder.weight".into())?,
466 patch * patch * in_channels,
467 ),
468 x_emb_b: take("x_embedder.bias".into())?,
469 t_lin1_w: take("time_caption_embed.timestep_embedder.linear_1.weight".into())?,
470 t_lin1_b: take("time_caption_embed.timestep_embedder.linear_1.bias".into())?,
471 t_lin2_w: take("time_caption_embed.timestep_embedder.linear_2.weight".into())?,
472 t_lin2_b: take("time_caption_embed.timestep_embedder.linear_2.bias".into())?,
473 cap_norm,
474 cap_w: Proj::f32(
475 take("time_caption_embed.caption_embedder.1.weight".into())?,
476 cap_feat,
477 ),
478 cap_b: take("time_caption_embed.caption_embedder.1.bias".into())?,
479 context_refiner,
480 noise_refiner,
481 layers,
482 out_lin1_w: take("norm_out.linear_1.weight".into())?,
483 out_lin1_b: take("norm_out.linear_1.bias".into())?,
484 out_lin2: Proj::f32(take("norm_out.linear_2.weight".into())?, hidden),
485 out_lin2_b: take("norm_out.linear_2.bias".into())?,
486 pool: Pool::from_env(),
487 hidden,
488 in_channels,
489 patch,
490 nh,
491 nkv: cfg["num_kv_heads"].as_u64().unwrap_or(nh as u64) as usize,
492 hd: hidden / nh,
493 axes_dim,
494 eps: cfg["norm_eps"].as_f64().unwrap_or(1e-5),
495 })
496 }
497
498 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
501 let cfg: serde_json::Value = serde_json::from_slice(
502 model
503 .tensor_bytes("dit.config_json")
504 .map_err(|e| e.to_string())?,
505 )
506 .map_err(|e| format!("dit.config_json: {e}"))?;
507 let f32v = |n: &str| -> Result<Vec<f32>, String> { cmf_f32(model, n) };
508 let hidden = cfg["hidden_size"].as_u64().ok_or("hidden")? as usize;
509 let blocks = |pfx: &str, count: usize, modulated: bool| -> Result<Vec<Block>, String> {
510 (0..count)
511 .map(|l| {
512 let p = format!("dit.{pfx}.{l}");
513 Ok(Block {
514 modulation: if modulated {
515 Some((
516 Proj::from_model(model, &format!("{p}.norm1.linear.weight"))?,
517 f32v(&format!("{p}.norm1.linear.bias"))?,
518 ))
519 } else {
520 None
521 },
522 norm1: if modulated {
523 f32v(&format!("{p}.norm1.norm.weight"))?
524 } else {
525 f32v(&format!("{p}.norm1.weight"))?
526 },
527 q: Proj::from_model(model, &format!("{p}.attn.to_q.weight"))?,
528 k: Proj::from_model(model, &format!("{p}.attn.to_k.weight"))?,
529 v: Proj::from_model(model, &format!("{p}.attn.to_v.weight"))?,
530 o: Proj::from_model(model, &format!("{p}.attn.to_out.0.weight"))?,
531 norm_q: f32v(&format!("{p}.attn.norm_q.weight"))?,
532 norm_k: f32v(&format!("{p}.attn.norm_k.weight"))?,
533 norm2: f32v(&format!("{p}.norm2.weight"))?,
534 ffn_norm1: f32v(&format!("{p}.ffn_norm1.weight"))?,
535 w1: Proj::from_model(model, &format!("{p}.feed_forward.linear_1.weight"))?,
536 w3: Proj::from_model(model, &format!("{p}.feed_forward.linear_3.weight"))?,
537 w2: Proj::from_model(model, &format!("{p}.feed_forward.linear_2.weight"))?,
538 ffn_norm2: f32v(&format!("{p}.ffn_norm2.weight"))?,
539 })
540 })
541 .collect()
542 };
543 let nl = cfg["num_layers"].as_u64().ok_or("num_layers")? as usize;
544 let nr = cfg["num_refiner_layers"].as_u64().unwrap_or(2) as usize;
545 let nh = cfg["num_attention_heads"].as_u64().ok_or("nh")? as usize;
546 let axes_dim: Vec<usize> = cfg["axes_dim_rope"]
547 .as_array()
548 .ok_or("axes_dim_rope")?
549 .iter()
550 .map(|v| v.as_u64().unwrap_or(0) as usize)
551 .collect();
552 Ok(Self {
553 x_emb: Proj::from_model(model, "dit.x_embedder.weight")?,
554 x_emb_b: f32v("dit.x_embedder.bias")?,
555 t_lin1_w: f32v("dit.time_caption_embed.timestep_embedder.linear_1.weight")?,
556 t_lin1_b: f32v("dit.time_caption_embed.timestep_embedder.linear_1.bias")?,
557 t_lin2_w: f32v("dit.time_caption_embed.timestep_embedder.linear_2.weight")?,
558 t_lin2_b: f32v("dit.time_caption_embed.timestep_embedder.linear_2.bias")?,
559 cap_norm: f32v("dit.time_caption_embed.caption_embedder.0.weight")?,
560 cap_w: Proj::from_model(model, "dit.time_caption_embed.caption_embedder.1.weight")?,
561 cap_b: f32v("dit.time_caption_embed.caption_embedder.1.bias")?,
562 context_refiner: blocks("context_refiner", nr, false)?,
563 noise_refiner: blocks("noise_refiner", nr, true)?,
564 layers: blocks("layers", nl, true)?,
565 out_lin1_w: f32v("dit.norm_out.linear_1.weight")?,
566 out_lin1_b: f32v("dit.norm_out.linear_1.bias")?,
567 out_lin2: Proj::from_model(model, "dit.norm_out.linear_2.weight")?,
568 out_lin2_b: f32v("dit.norm_out.linear_2.bias")?,
569 pool: Pool::from_env(),
570 hidden,
571 in_channels: cfg["in_channels"].as_u64().ok_or("in_channels")? as usize,
572 patch: cfg["patch_size"].as_u64().unwrap_or(2) as usize,
573 nh,
574 nkv: cfg["num_kv_heads"].as_u64().unwrap_or(nh as u64) as usize,
575 hd: hidden / nh,
576 axes_dim,
577 eps: cfg["norm_eps"].as_f64().unwrap_or(1e-5),
578 })
579 }
580
581 fn time_embed(&self, t: f32) -> Vec<f32> {
583 const HALF: usize = 128;
584 let mut freq = [0f32; 2 * HALF];
585 for i in 0..HALF {
586 let ang = t as f64 * (-(10000f64.ln()) * i as f64 / HALF as f64).exp();
587 freq[i] = ang.cos() as f32;
588 freq[HALF + i] = ang.sin() as f32;
589 }
590 let mut h = linear(&freq, &self.t_lin1_w, &self.t_lin1_b);
591 for v in h.iter_mut() {
592 *v = silu(*v);
593 }
594 linear(&h, &self.t_lin2_w, &self.t_lin2_b)
595 }
596
597 fn gpu_ffn(&self, blk: &Block, xn: &[f32], n: usize, out: &mut [f32]) -> bool {
604 use crate::gpu;
605 if n < 128 || !gpu::enabled_here() || gpu::mm_killed() {
606 return false;
607 }
608 if !gpu::fused_block_trusted()
610 && (gpu::probe_deciding(gpu::OpClass::MatmatWide)
611 || !matches!(gpu::probe_arm(gpu::OpClass::MatmatWide), gpu::ProbeArm::Gpu))
612 {
613 return false;
614 }
615 let (Proj::Q(q1), Proj::Q(q3), Proj::Q(q2)) = (&blk.w1, &blk.w3, &blk.w2) else {
616 return false;
617 };
618 let tp = q1.mapped_q4tp().is_some();
623 let (Some((m, i1)), Some((_, i3)), Some((_, i2))) = (if tp {
624 (q1.mapped_q4tp(), q3.mapped_q4tp(), q2.mapped_q4tp())
625 } else {
626 (q1.mapped_q4t(), q3.mapped_q4t(), q2.mapped_q4t())
627 }) else {
628 return false;
629 };
630 let inter = q1.rows();
631 let t0 = std::time::Instant::now();
632 let ok = if tp {
633 gpu::q4tp_ffn(m, i1, i3, i2, xn, n, self.hidden, inter, out)
634 } else {
635 gpu::q4t_ffn(m, i1, i3, i2, xn, n, self.hidden, inter, out)
636 };
637 if !ok {
638 return false;
639 }
640 let flops = 6.0 * n as f64 * self.hidden as f64 * inter as f64;
641 let budget = std::time::Duration::from_secs_f64(flops / 1.5e12 * 8.0 + 0.020);
642 let el = t0.elapsed();
643 gpu::mm_budget_check("ffn", el, budget, gpu::probe_was_cold());
644 true
645 }
646
647 fn gpu_attention(
653 &self,
654 q_all: &[f32],
655 k_all: &[f32],
656 v_all: &[f32],
657 n: usize,
658 scale: f32,
659 attn: &mut [f32],
660 ) -> bool {
661 use crate::gpu;
662 let (nh, nkv, hd) = (self.nh, self.nkv, self.hd);
663 if n < 128 || !gpu::enabled_here() || gpu::mm_killed() {
664 return false;
665 }
666 if !gpu::fused_block_trusted()
668 && (gpu::probe_deciding(gpu::OpClass::MatmatWide)
669 || !matches!(gpu::probe_arm(gpu::OpClass::MatmatWide), gpu::ProbeArm::Gpu))
670 {
671 return false;
672 }
673 let pool = self.pool.as_deref();
674 let mut qh = vec![0f32; nh * n * hd];
675 let mut kh = vec![0f32; nkv * n * hd];
676 let mut vh = vec![0f32; nkv * n * hd];
677 {
678 let _s = prof::span(prof::APACK);
679 let (sq, sk, sv) = (
680 SendRows(qh.as_mut_ptr()),
681 SendRows(kh.as_mut_ptr()),
682 SendRows(vh.as_mut_ptr()),
683 );
684 pool_rows(pool, n, &|start, end| {
685 for p in start..end {
686 for h in 0..nh {
687 unsafe { sq.row((h * n + p) * hd, hd) }
689 .copy_from_slice(&q_all[(p * nh + h) * hd..(p * nh + h + 1) * hd]);
690 }
691 for h in 0..nkv {
692 unsafe { sk.row((h * n + p) * hd, hd) }
693 .copy_from_slice(&k_all[(p * nkv + h) * hd..(p * nkv + h + 1) * hd]);
694 unsafe { sv.row((h * n + p) * hd, hd) }
695 .copy_from_slice(&v_all[(p * nkv + h) * hd..(p * nkv + h + 1) * hd]);
696 }
697 }
698 });
699 }
700 let _s = prof::span(prof::AQK);
701 let t0 = std::time::Instant::now();
702 if !gpu::dit_attention(&qh, &kh, &vh, nh, nkv, n, hd, scale, attn) {
703 return false;
704 }
705 let flops = 4.0 * nh as f64 * (n as f64) * (n as f64) * hd as f64;
706 let budget = std::time::Duration::from_secs_f64(flops / 1.5e12 * 8.0 + 0.020);
707 let el = t0.elapsed();
708 gpu::mm_budget_check("attention", el, budget, gpu::probe_was_cold());
709 true
710 }
711
712 fn gpu_block(
716 &self,
717 blk: &Block,
718 x: &mut [f32],
719 n: usize,
720 rope32: &(Vec<f32>, Vec<f32>),
721 m: &[f32],
722 ) -> bool {
723 self.gpu_block_seg(blk, x, n, rope32, m, &[n], (false, false))
724 }
725
726 #[allow(clippy::too_many_arguments)]
727 fn gpu_block_seg(
728 &self,
729 blk: &Block,
730 x: &mut [f32],
731 n: usize,
732 rope32: &(Vec<f32>, Vec<f32>),
733 m: &[f32],
734 segs: &[usize],
735 resident: (bool, bool),
736 ) -> bool {
737 use crate::gpu;
738 let (hs, nh, nkv, hd) = (self.hidden, self.nh, self.nkv, self.hd);
739 if n < 128 || !gpu::enabled_here() || gpu::mm_killed() {
740 return false;
741 }
742 if !gpu::fused_block_trusted()
744 && (gpu::probe_deciding(gpu::OpClass::MatmatWide)
745 || !matches!(gpu::probe_arm(gpu::OpClass::MatmatWide), gpu::ProbeArm::Gpu))
746 {
747 return false;
748 }
749 if rope32.0.len() != n * hd / 2 {
752 return false;
753 }
754 fn q(p: &Proj) -> Option<(&Arc<CmfModel>, usize)> {
758 match p {
759 Proj::Q(q) => q.mapped_q4t().or_else(|| q.mapped_q4tp()),
760 Proj::F32 { .. } => None,
761 }
762 }
763 let is_q4tp = matches!(&blk.q, Proj::Q(q) if q.mapped_q4tp().is_some());
764 let (
765 Some((model, wq)),
766 Some((_, wk)),
767 Some((_, wv)),
768 Some((_, wo)),
769 Some((_, w1)),
770 Some((_, w3)),
771 Some((_, w2)),
772 ) = (
773 q(&blk.q),
774 q(&blk.k),
775 q(&blk.v),
776 q(&blk.o),
777 q(&blk.w1),
778 q(&blk.w3),
779 q(&blk.w2),
780 )
781 else {
782 return false;
783 };
784 let inter = blk.w1.rows();
785 let gate_msa: Vec<f32> = m[hs..2 * hs].iter().map(|&v| v.tanh()).collect();
786 let gate_mlp: Vec<f32> = m[3 * hs..].iter().map(|&v| v.tanh()).collect();
787 let args = gpu::DitBlockArgs {
788 q4tp: is_q4tp,
789 resident_in: resident.0,
790 resident_out: resident.1,
791 n,
792 hidden: hs,
793 inter,
794 nh,
795 nkv,
796 hd,
797 eps: self.eps as f32,
798 rope_cos: &rope32.0,
799 rope_sin: &rope32.1,
800 norm1: &blk.norm1,
801 norm2: &blk.norm2,
802 ffn_norm1: &blk.ffn_norm1,
803 ffn_norm2: &blk.ffn_norm2,
804 norm_q: &blk.norm_q,
805 norm_k: &blk.norm_k,
806 s_msa: &m[..hs],
807 gate_msa: &gate_msa,
808 s_mlp: &m[2 * hs..3 * hs],
809 gate_mlp: &gate_mlp,
810 wq,
811 wk,
812 wv,
813 wo,
814 w1,
815 w3,
816 w2,
817 };
818 let t0 = std::time::Instant::now();
819 if !gpu::dit_block_seg(model, &args, segs, x) {
820 return false;
821 }
822 let flops = 2.0 * n as f64 * hs as f64 * ((nh + 2 * nkv) * hd) as f64
823 + 4.0 * nh as f64 * (n as f64) * (n as f64) * hd as f64
824 + 2.0 * n as f64 * hs as f64 * (nh * hd) as f64
825 + 6.0 * n as f64 * hs as f64 * inter as f64;
826 let budget = std::time::Duration::from_secs_f64(flops / 1.5e12 * 8.0 + 0.030);
827 let el = t0.elapsed();
828 gpu::mm_budget_check("dit block", el, budget, gpu::probe_was_cold());
829 true
830 }
831
832 fn attention_seq(
837 &self,
838 q_all: &[f32],
839 k_all: &[f32],
840 v_all: &[f32],
841 n: usize,
842 scale: f32,
843 attn: &mut [f32],
844 ) {
845 let (nh, nkv, hd) = (self.nh, self.nkv, self.hd);
846 let hpk = nh / nkv;
847 let pool = self.pool.as_deref();
848 let mut qh = vec![0f32; n * hd];
849 let mut kh = vec![0f32; n * hd];
850 let mut vt = vec![0f32; hd * n]; let mut scores = vec![0f32; n * n];
852 let mut oh = vec![0f32; n * hd];
853 for hh in 0..nh {
854 let kv = hh / hpk;
855 {
856 let _s = prof::span(prof::APACK);
857 let (sq, sk, sv) = (
858 SendRows(qh.as_mut_ptr()),
859 SendRows(kh.as_mut_ptr()),
860 SendRows(vt.as_mut_ptr()),
861 );
862 pool_rows(pool, n, &|start, end| {
863 for p in start..end {
864 let qsrc = &q_all[(p * nh + hh) * hd..(p * nh + hh + 1) * hd];
865 let qd = unsafe { sq.row(p * hd, hd) };
868 for (d, &v) in qsrc.iter().enumerate() {
869 qd[d] = v * scale;
870 }
871 unsafe { sk.row(p * hd, hd) }
872 .copy_from_slice(&k_all[(p * nkv + kv) * hd..(p * nkv + kv + 1) * hd]);
873 let vv = &v_all[(p * nkv + kv) * hd..(p * nkv + kv + 1) * hd];
874 for (d, &val) in vv.iter().enumerate() {
875 unsafe { sv.set(d * n + p, val) };
876 }
877 }
878 });
879 }
880 {
881 let _s = prof::span(prof::AQK);
882 crate::fcd_ops::gemm_nt(&qh, &kh, &mut scores, n, hd, n, pool);
883 }
884 {
885 let _s = prof::span(prof::SOFTMAX);
886 let sp = SendRows(scores.as_mut_ptr());
887 let soft = |start: usize, end: usize| {
888 for r in start..end {
889 softmax_inplace(unsafe { sp.row(r * n, n) });
891 }
892 };
893 match pool {
894 Some(p) => p.run_rows(n, &soft),
895 None => soft(0, n),
896 }
897 }
898 {
899 let _s = prof::span(prof::APV);
900 crate::fcd_ops::gemm_nt(&scores, &vt, &mut oh, n, n, hd, pool);
901 }
902 let _s = prof::span(prof::APACK);
903 let sa = SendRows(attn.as_mut_ptr());
904 pool_rows(pool, n, &|start, end| {
905 for p in start..end {
906 unsafe { sa.row((p * nh + hh) * hd, hd) }
908 .copy_from_slice(&oh[p * hd..(p + 1) * hd]);
909 }
910 });
911 }
912 }
913
914 fn block_forward(
915 &self,
916 blk: &Block,
917 x: &mut [f32],
918 rope: &(Vec<f64>, Vec<f64>),
919 rope32: Option<&(Vec<f32>, Vec<f32>)>,
920 temb: Option<&[f32]>,
921 ) {
922 let n_all = x.len() / self.hidden;
923 let _ = self.block_forward_seg(blk, x, rope, rope32, temb, &[n_all], (false, false));
924 }
925
926 fn block_forward_seg(
934 &self,
935 blk: &Block,
936 x: &mut [f32],
937 rope: &(Vec<f64>, Vec<f64>),
938 rope32: Option<&(Vec<f32>, Vec<f32>)>,
939 temb: Option<&[f32]>,
940 segs: &[usize],
941 resident: (bool, bool),
942 ) -> bool {
943 let (hs, nh, nkv, hd) = (self.hidden, self.nh, self.nkv, self.hd);
944 let pool = self.pool.as_deref();
945 let n = x.len() / hs;
946 let modv = {
947 let _s = prof::span(prof::MODNORM);
948 blk.modulation.as_ref().zip(temb).map(|((w, b), t)| {
949 let s: Vec<f32> = t.iter().map(|&v| silu(v)).collect();
950 let mut m = vec![0f32; w.rows()];
951 w.matmat(&s, 1, &mut m, pool);
952 for (v, &bias) in m.iter_mut().zip(b) {
953 *v += bias;
954 }
955 m
956 })
957 };
958 if let (Some(m), Some(r32)) = (&modv, rope32) {
959 let _s = prof::span(prof::GPUBLK);
960 if self.gpu_block_seg(blk, x, n, r32, m, segs, resident) {
961 return true;
962 }
963 }
964 if resident.0 {
967 crate::gpu::dit_state_fetch(&mut x[..n * hs]);
968 }
969 let modnorm = prof::span(prof::MODNORM);
970 let (s_msa, g_msa, s_mlp, g_mlp) = match &modv {
971 Some(m) => (
972 Some(&m[..hs]),
973 Some(&m[hs..2 * hs]),
974 Some(&m[2 * hs..3 * hs]),
975 Some(&m[3 * hs..]),
976 ),
977 None => (None, None, None, None),
978 };
979 let gate_msa: Option<Vec<f32>> = g_msa.map(|g| g.iter().map(|&v| v.tanh()).collect());
983 let gate_mlp: Option<Vec<f32>> = g_mlp.map(|g| g.iter().map(|&v| v.tanh()).collect());
984 let norm_scaled = |src: &[f32], w: &[f32], s: Option<&[f32]>, dst: &mut [f32]| {
989 let sr = SendRows(dst.as_mut_ptr());
990 pool_rows(pool, n, &|start, end| {
991 for p in start..end {
992 let row = unsafe { sr.row(p * hs, hs) };
994 rms_norm_into(&src[p * hs..(p + 1) * hs], w, self.eps, row);
995 if let Some(s) = s {
996 for (r, &sc) in row.iter_mut().zip(s) {
997 *r *= 1.0 + sc;
998 }
999 }
1000 }
1001 });
1002 };
1003 let residual = |src: &[f32], w: &[f32], gate: Option<&[f32]>, x: &mut [f32]| {
1004 let sr = SendRows(x.as_mut_ptr());
1005 pool_rows(pool, n, &|start, end| {
1006 let mut tmp = vec![0f32; hs];
1007 for p in start..end {
1008 rms_norm_into(&src[p * hs..(p + 1) * hs], w, self.eps, &mut tmp);
1009 let dst = unsafe { sr.row(p * hs, hs) };
1011 match gate {
1012 Some(g) => {
1013 for ((d, &v), >) in dst.iter_mut().zip(&tmp).zip(g) {
1014 *d += gt * v;
1015 }
1016 }
1017 None => {
1018 for (d, &v) in dst.iter_mut().zip(&tmp) {
1019 *d += v;
1020 }
1021 }
1022 }
1023 }
1024 });
1025 };
1026 let mut xn = vec![0f32; n * hs];
1028 norm_scaled(x, &blk.norm1, s_msa, &mut xn);
1029 drop(modnorm);
1030 let mut q_all = vec![0f32; n * nh * hd];
1031 let mut k_all = vec![0f32; n * nkv * hd];
1032 let mut v_all = vec![0f32; n * nkv * hd];
1033 {
1034 let _s = prof::span(prof::QKV);
1035 let fused = match (&blk.q, &blk.k, &blk.v) {
1039 (Proj::Q(q), Proj::Q(k), Proj::Q(v))
1040 if n >= 128 && crate::gpu::enabled_here() && !crate::gpu::mm_killed() =>
1041 {
1042 match (q.model_arc(), q.model_idx(), k.model_idx(), v.model_idx()) {
1043 (Some(m), Some(iq), Some(ik), Some(iv)) => crate::gpu::dit_qkv(
1044 &m,
1045 iq,
1046 ik,
1047 iv,
1048 &xn,
1049 n,
1050 hs,
1051 nh * hd,
1052 nkv * hd,
1053 &mut q_all,
1054 &mut k_all,
1055 &mut v_all,
1056 ),
1057 _ => false,
1058 }
1059 }
1060 _ => false,
1061 };
1062 if !fused {
1063 blk.q.matmat(&xn, n, &mut q_all, pool);
1064 blk.k.matmat(&xn, n, &mut k_all, pool);
1065 blk.v.matmat(&xn, n, &mut v_all, pool);
1066 }
1067 }
1068 let rope_span = prof::span(prof::ROPE);
1069 let (cos, sin) = rope;
1071 let pairs = hd / 2;
1072 for (all, heads, w) in [
1073 (&mut q_all, nh, &blk.norm_q),
1074 (&mut k_all, nkv, &blk.norm_k),
1075 ] {
1076 let sr = SendRows(all.as_mut_ptr());
1077 pool_rows(pool, n, &|start, end| {
1078 for p in start..end {
1079 for hh in 0..heads {
1080 let v = unsafe { sr.row((p * heads + hh) * hd, hd) };
1082 rms_norm_inplace(v, w, 1e-5);
1083 for j in 0..pairs {
1084 let (c, s) = (cos[p * pairs + j], sin[p * pairs + j]);
1085 let (a, b) = (v[2 * j] as f64, v[2 * j + 1] as f64);
1086 v[2 * j] = (a * c - b * s) as f32;
1087 v[2 * j + 1] = (a * s + b * c) as f32;
1088 }
1089 }
1090 }
1091 });
1092 }
1093 drop(rope_span);
1094 let scale = 1.0 / (hd as f32).sqrt();
1100 let _hpk = nh / nkv;
1101 let mut attn = vec![0f32; n * nh * hd];
1102 if segs.len() > 1 {
1103 let mut off = 0usize;
1107 for &ns in segs {
1108 let (qs, ks, vs) = (
1109 &q_all[off * nh * hd..(off + ns) * nh * hd],
1110 &k_all[off * nkv * hd..(off + ns) * nkv * hd],
1111 &v_all[off * nkv * hd..(off + ns) * nkv * hd],
1112 );
1113 let dst = &mut attn[off * nh * hd..(off + ns) * nh * hd];
1114 if !self.gpu_attention(qs, ks, vs, ns, scale, dst) {
1115 self.attention_seq(qs, ks, vs, ns, scale, dst);
1116 }
1117 off += ns;
1118 }
1119 } else if !self.gpu_attention(&q_all, &k_all, &v_all, n, scale, &mut attn) {
1120 self.attention_seq(&q_all, &k_all, &v_all, n, scale, &mut attn);
1121 }
1122 let mut proj = vec![0f32; n * hs];
1123 {
1124 let _s = prof::span(prof::OPROJ);
1125 blk.o.matmat(&attn, n, &mut proj, pool);
1126 }
1127 let modnorm = prof::span(prof::MODNORM);
1128 residual(&proj, &blk.norm2, gate_msa.as_deref(), x);
1129 norm_scaled(x, &blk.ffn_norm1, s_mlp, &mut xn);
1131 drop(modnorm);
1132 let mut d_all = vec![0f32; n * hs];
1133 let fused = {
1134 let _s = prof::span(prof::FFN);
1135 self.gpu_ffn(blk, &xn, n, &mut d_all)
1136 };
1137 if !fused {
1138 let inter = blk.w1.rows();
1139 let mut g_all = vec![0f32; n * inter];
1140 let mut u_all = vec![0f32; n * inter];
1141 {
1142 let _s = prof::span(prof::FFN);
1143 blk.w1.matmat(&xn, n, &mut g_all, pool);
1144 blk.w3.matmat(&xn, n, &mut u_all, pool);
1145 }
1146 {
1147 let _s = prof::span(prof::FFNEL);
1148 let sg = SendRows(g_all.as_mut_ptr());
1149 pool_rows(pool, n, &|start, end| {
1150 for p in start..end {
1151 let g = unsafe { sg.row(p * inter, inter) };
1153 for (gv, &uv) in g.iter_mut().zip(&u_all[p * inter..(p + 1) * inter]) {
1154 *gv = silu(*gv) * uv;
1155 }
1156 }
1157 });
1158 }
1159 {
1160 let _s = prof::span(prof::FFN);
1161 blk.w2.matmat(&g_all, n, &mut d_all, pool);
1162 }
1163 }
1164 let _modnorm = prof::span(prof::MODNORM);
1165 residual(&d_all, &blk.ffn_norm2, gate_mlp.as_deref(), x);
1166 false
1167 }
1168
1169 pub fn forward(
1173 &self,
1174 latent: &[f32],
1175 h: usize,
1176 w: usize,
1177 cap: &[f32],
1178 cap_n: usize,
1179 t: f32,
1180 ) -> Vec<f32> {
1181 self.forward_with_cap(latent, h, w, &self.refine_caption(cap, cap_n), cap_n, t)
1182 }
1183
1184 pub fn refine_caption(&self, cap: &[f32], cap_n: usize) -> Vec<f32> {
1192 let hs = self.hidden;
1193 let cap_feat = self.cap_norm.len();
1194 let mut cap_n_all = vec![0f32; cap_n * cap_feat];
1195 for i in 0..cap_n {
1196 cap_n_all[i * cap_feat..(i + 1) * cap_feat].copy_from_slice(&rms_norm(
1197 &cap[i * cap_feat..(i + 1) * cap_feat],
1198 &self.cap_norm,
1199 self.eps,
1200 ));
1201 }
1202 let mut cap_e = vec![0f32; cap_n * hs];
1203 self.cap_w
1204 .matmat(&cap_n_all, cap_n, &mut cap_e, self.pool.as_deref());
1205 for i in 0..cap_n {
1206 for (v, &b) in cap_e[i * hs..(i + 1) * hs].iter_mut().zip(&self.cap_b) {
1207 *v += b;
1208 }
1209 }
1210 let cap_ids: Vec<[u32; 3]> = (0..cap_n).map(|i| [i as u32, 0, 0]).collect();
1211 let cap_rope = rope_table(&cap_ids, &self.axes_dim);
1212 for blk in &self.context_refiner {
1213 self.block_forward(blk, &mut cap_e, &cap_rope, None, None);
1214 }
1215 cap_e
1216 }
1217
1218 #[allow(clippy::too_many_arguments)]
1228 pub fn forward_cfg_pair(
1229 &self,
1230 latent: &[f32],
1231 h: usize,
1232 w: usize,
1233 cap_c: &[f32],
1234 cap_c_n: usize,
1235 cap_u: &[f32],
1236 cap_u_n: usize,
1237 t: f32,
1238 ) -> (Vec<f32>, Vec<f32>) {
1239 let (c, p, hs) = (self.in_channels, self.patch, self.hidden);
1240 let (hp, wp) = (h / p, w / p);
1241 let n_img = hp * wp;
1242 let head = prof::span(prof::HEADTAIL);
1243 let temb = self.time_embed(t);
1244 let pv = p * p * c;
1245 let mut tok = vec![0f32; n_img * pv];
1246 for ph in 0..hp {
1247 for pw in 0..wp {
1248 let dst = &mut tok[(ph * wp + pw) * pv..(ph * wp + pw + 1) * pv];
1249 for dy in 0..p {
1250 for dx in 0..p {
1251 for ch in 0..c {
1252 dst[(dy * p + dx) * c + ch] =
1253 latent[ch * h * w + (ph * p + dy) * w + pw * p + dx];
1254 }
1255 }
1256 }
1257 }
1258 }
1259 let mut img = vec![0f32; n_img * hs];
1260 self.x_emb
1261 .matmat(&tok, n_img, &mut img, self.pool.as_deref());
1262 for i in 0..n_img {
1263 for (v, &b) in img[i * hs..(i + 1) * hs].iter_mut().zip(&self.x_emb_b) {
1264 *v += b;
1265 }
1266 }
1267 let img_ids: Vec<[u32; 3]> = (0..n_img)
1268 .map(|i| [0, (i / wp) as u32, (i % wp) as u32])
1269 .collect();
1270 let to32 = |r: &(Vec<f64>, Vec<f64>)| {
1271 (
1272 r.0.iter().map(|&v| v as f32).collect::<Vec<f32>>(),
1273 r.1.iter().map(|&v| v as f32).collect::<Vec<f32>>(),
1274 )
1275 };
1276 let img_rope_r = rope_table(
1278 &(0..n_img)
1279 .map(|i| [0u32, (i / wp) as u32, (i % wp) as u32])
1280 .collect::<Vec<_>>(),
1281 &self.axes_dim,
1282 );
1283 let img_rope32_r = to32(&img_rope_r);
1284 drop(head);
1285 for blk in &self.noise_refiner {
1286 self.block_forward(blk, &mut img, &img_rope_r, Some(&img_rope32_r), Some(&temb));
1287 }
1288 let _ = img_ids;
1289 let n_c = cap_c_n + n_img;
1291 let n_u = cap_u_n + n_img;
1292 let mut x = Vec::with_capacity((n_c + n_u) * hs);
1293 x.extend_from_slice(&cap_c[..cap_c_n * hs]);
1294 x.extend_from_slice(&img);
1295 x.extend_from_slice(&cap_u[..cap_u_n * hs]);
1296 x.extend_from_slice(&img);
1297 let rope_for = |cap_n: usize| -> (Vec<f64>, Vec<f64>) {
1298 let cap_ids: Vec<[u32; 3]> = (0..cap_n).map(|i| [i as u32, 0, 0]).collect();
1299 let im_ids: Vec<[u32; 3]> = (0..n_img)
1300 .map(|i| [cap_n as u32, (i / wp) as u32, (i % wp) as u32])
1301 .collect();
1302 let a = rope_table(&cap_ids, &self.axes_dim);
1303 let b = rope_table(&im_ids, &self.axes_dim);
1304 ([a.0, b.0].concat(), [a.1, b.1].concat())
1305 };
1306 let (rc, ru) = (rope_for(cap_c_n), rope_for(cap_u_n));
1307 let joint_rope = ([rc.0, ru.0].concat(), [rc.1, ru.1].concat());
1308 let joint_rope32 = to32(&joint_rope);
1309 let segs = [n_c, n_u];
1310 let chain = crate::gpu::dit_chain_supported();
1315 let last = self.layers.len().saturating_sub(1);
1316 let mut resident = false;
1317 for (i, blk) in self.layers.iter().enumerate() {
1318 let want = if chain {
1319 (resident, i != last)
1320 } else {
1321 (false, false)
1322 };
1323 let on_gpu = self.block_forward_seg(
1326 blk,
1327 &mut x,
1328 &joint_rope,
1329 Some(&joint_rope32),
1330 Some(&temb),
1331 &segs,
1332 want,
1333 );
1334 resident = on_gpu && want.1;
1335 }
1336 let _tail = prof::span(prof::HEADTAIL);
1337 let n = n_c + n_u;
1338 let silu_t: Vec<f32> = temb.iter().map(|&v| silu(v)).collect();
1339 let scale = linear(&silu_t, &self.out_lin1_w, &self.out_lin1_b);
1340 for row in x.chunks_exact_mut(hs) {
1341 let mean = row.iter().map(|&v| v as f64).sum::<f64>() / hs as f64;
1342 let var = row
1343 .iter()
1344 .map(|&v| (v as f64 - mean) * (v as f64 - mean))
1345 .sum::<f64>()
1346 / hs as f64;
1347 let inv = 1.0 / (var + 1e-6).sqrt();
1348 for (v, &s) in row.iter_mut().zip(&scale) {
1349 *v = ((*v as f64 - mean) * inv) as f32 * (1.0 + s);
1350 }
1351 }
1352 let mut out = vec![0f32; n * pv];
1353 self.out_lin2.matmat(&x, n, &mut out, self.pool.as_deref());
1354 for i in 0..n {
1355 for (v, &b) in out[i * pv..(i + 1) * pv].iter_mut().zip(&self.out_lin2_b) {
1356 *v += b;
1357 }
1358 }
1359 let unpatch = |base: usize| -> Vec<f32> {
1360 let mut pred = vec![0f32; c * h * w];
1361 for ph in 0..hp {
1362 for pw in 0..wp {
1363 let src = &out[(base + ph * wp + pw) * pv..(base + ph * wp + pw + 1) * pv];
1364 for dy in 0..p {
1365 for dx in 0..p {
1366 for ch in 0..c {
1367 pred[ch * h * w + (ph * p + dy) * w + pw * p + dx] =
1368 src[(dy * p + dx) * c + ch];
1369 }
1370 }
1371 }
1372 }
1373 }
1374 pred
1375 };
1376 (unpatch(cap_c_n), unpatch(n_c + cap_u_n))
1377 }
1378
1379 pub fn forward_with_cap(
1380 &self,
1381 latent: &[f32],
1382 h: usize,
1383 w: usize,
1384 cap_e_in: &[f32],
1385 cap_n: usize,
1386 t: f32,
1387 ) -> Vec<f32> {
1388 let (c, p, hs) = (self.in_channels, self.patch, self.hidden);
1389 assert_eq!(latent.len(), c * h * w);
1390 let (hp, wp) = (h / p, w / p);
1391 let n_img = hp * wp;
1392 let head = prof::span(prof::HEADTAIL);
1393 let temb = self.time_embed(t);
1394 let cap_e = cap_e_in.to_vec();
1395
1396 let pv = p * p * c;
1398 let mut tok = vec![0f32; n_img * pv];
1399 for ph in 0..hp {
1400 for pw in 0..wp {
1401 let dst = &mut tok[(ph * wp + pw) * pv..(ph * wp + pw + 1) * pv];
1402 for dy in 0..p {
1403 for dx in 0..p {
1404 for ch in 0..c {
1405 dst[(dy * p + dx) * c + ch] =
1406 latent[ch * h * w + (ph * p + dy) * w + pw * p + dx];
1407 }
1408 }
1409 }
1410 }
1411 }
1412 let mut img = vec![0f32; n_img * hs];
1413 self.x_emb
1414 .matmat(&tok, n_img, &mut img, self.pool.as_deref());
1415 for i in 0..n_img {
1416 for (v, &b) in img[i * hs..(i + 1) * hs].iter_mut().zip(&self.x_emb_b) {
1417 *v += b;
1418 }
1419 }
1420
1421 let cap_ids: Vec<[u32; 3]> = (0..cap_n).map(|i| [i as u32, 0, 0]).collect();
1423 let img_ids: Vec<[u32; 3]> = (0..n_img)
1424 .map(|i| [cap_n as u32, (i / wp) as u32, (i % wp) as u32])
1425 .collect();
1426 let cap_rope = rope_table(&cap_ids, &self.axes_dim);
1427 let img_rope = rope_table(&img_ids, &self.axes_dim);
1428 let to32 = |r: &(Vec<f64>, Vec<f64>)| {
1430 (
1431 r.0.iter().map(|&v| v as f32).collect::<Vec<f32>>(),
1432 r.1.iter().map(|&v| v as f32).collect::<Vec<f32>>(),
1433 )
1434 };
1435 let img_rope32 = to32(&img_rope);
1436 drop(head);
1437
1438 for blk in &self.noise_refiner {
1441 self.block_forward(blk, &mut img, &img_rope, Some(&img_rope32), Some(&temb));
1442 }
1443
1444 let n = cap_n + n_img;
1446 let mut x = cap_e;
1447 x.extend_from_slice(&img);
1448 let joint_rope = (
1449 [cap_rope.0, img_rope.0].concat(),
1450 [cap_rope.1, img_rope.1].concat(),
1451 );
1452 let joint_rope32 = to32(&joint_rope);
1453 for blk in &self.layers {
1454 self.block_forward(blk, &mut x, &joint_rope, Some(&joint_rope32), Some(&temb));
1455 }
1456
1457 let _tail = prof::span(prof::HEADTAIL);
1459 let silu_t: Vec<f32> = temb.iter().map(|&v| silu(v)).collect();
1460 let scale = linear(&silu_t, &self.out_lin1_w, &self.out_lin1_b);
1461 for row in x.chunks_exact_mut(hs) {
1462 let mean = row.iter().map(|&v| v as f64).sum::<f64>() / hs as f64;
1463 let var = row
1464 .iter()
1465 .map(|&v| (v as f64 - mean) * (v as f64 - mean))
1466 .sum::<f64>()
1467 / hs as f64;
1468 let inv = 1.0 / (var + 1e-6).sqrt();
1469 for (v, &s) in row.iter_mut().zip(&scale) {
1470 *v = ((*v as f64 - mean) * inv) as f32 * (1.0 + s);
1471 }
1472 }
1473 let mut out = vec![0f32; n * pv];
1474 self.out_lin2.matmat(&x, n, &mut out, self.pool.as_deref());
1475 for i in 0..n {
1476 for (v, &b) in out[i * pv..(i + 1) * pv].iter_mut().zip(&self.out_lin2_b) {
1477 *v += b;
1478 }
1479 }
1480
1481 let mut pred = vec![0f32; c * h * w];
1483 for ph in 0..hp {
1484 for pw in 0..wp {
1485 let src = &out[(cap_n + ph * wp + pw) * pv..(cap_n + ph * wp + pw + 1) * pv];
1486 for dy in 0..p {
1487 for dx in 0..p {
1488 for ch in 0..c {
1489 pred[ch * h * w + (ph * p + dy) * w + pw * p + dx] =
1490 src[(dy * p + dx) * c + ch];
1491 }
1492 }
1493 }
1494 }
1495 }
1496 pred
1497 }
1498}