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