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