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