1use crate::dit::Proj;
28use crate::gpu::{ZBlockRef, ZGeom};
29use crate::pool::Pool;
30use cortiq_core::CmfModel;
31use std::collections::HashMap;
32use std::path::Path;
33use std::sync::Arc;
34
35pub const SEQ_MULTI_OF: usize = 32;
37pub const DEFAULT_SHIFT: f32 = 3.0;
40pub const DEFAULT_STEPS: usize = 8;
42
43pub const ZGEOM_TURBO: ZGeom = ZGeom {
46 hidden: 3840,
47 nh: 30,
48 hd: 128,
49 inter: 10240,
50 eps: 1e-5,
51 final_eps: 1e-6,
52 patch_dim: 64,
53};
54
55#[derive(Clone, Debug)]
57pub struct ZConfig {
58 pub dim: usize,
60 pub n_layers: usize,
62 pub n_refiner: usize,
64 pub n_heads: usize,
66 pub n_kv_heads: usize,
67 pub head_dim: usize,
68 pub ffn_dim: usize,
70 pub cap_feat_dim: usize,
72 pub t_embed_dim: usize,
74 pub t_hidden: usize,
75 pub patch: usize,
77 pub in_channels: usize,
78 pub norm_eps: f32,
80 pub final_eps: f32,
81 pub rope_theta: f64,
83 pub axes_dims: [usize; 3],
84 pub t_scale: f32,
86}
87
88impl ZConfig {
89 pub fn from_json(v: &serde_json::Value) -> Result<Self, String> {
92 let u = |k: &str| -> Result<usize, String> {
93 v[k].as_u64()
94 .map(|x| x as usize)
95 .ok_or_else(|| format!("dit config: missing {k}"))
96 };
97 let dim = u("dim")?;
98 let n_heads = u("n_heads")?;
99 let axes: Vec<usize> = v["axes_dims"]
100 .as_array()
101 .ok_or("dit config: axes_dims")?
102 .iter()
103 .map(|x| x.as_u64().unwrap_or(0) as usize)
104 .collect();
105 if axes.len() != 3 {
106 return Err("dit config: axes_dims must have 3 entries".into());
107 }
108 let patch = v["all_patch_size"]
109 .as_array()
110 .and_then(|a| a.first())
111 .and_then(|x| x.as_u64())
112 .unwrap_or(2) as usize;
113 let head_dim = dim / n_heads;
114 if axes.iter().sum::<usize>() != head_dim {
115 return Err(format!(
116 "dit config: axes_dims {axes:?} do not sum to head_dim {head_dim}"
117 ));
118 }
119 Ok(Self {
120 dim,
121 n_layers: u("n_layers")?,
122 n_refiner: v["n_refiner_layers"].as_u64().unwrap_or(2) as usize,
123 n_heads,
124 n_kv_heads: v["n_kv_heads"].as_u64().unwrap_or(n_heads as u64) as usize,
125 head_dim,
126 ffn_dim: (dim as f64 / 3.0 * 8.0) as usize,
127 cap_feat_dim: u("cap_feat_dim")?,
128 t_embed_dim: dim.min(256),
129 t_hidden: 1024,
130 patch,
131 in_channels: v["in_channels"].as_u64().unwrap_or(16) as usize,
132 norm_eps: v["norm_eps"].as_f64().unwrap_or(1e-5) as f32,
133 final_eps: 1e-6,
134 rope_theta: v["rope_theta"].as_f64().unwrap_or(256.0),
135 axes_dims: [axes[0], axes[1], axes[2]],
136 t_scale: v["t_scale"].as_f64().unwrap_or(1000.0) as f32,
137 })
138 }
139
140 pub fn geom(&self) -> ZGeom {
141 ZGeom {
142 hidden: self.dim,
143 nh: self.n_heads,
144 hd: self.head_dim,
145 inter: self.ffn_dim,
146 eps: self.norm_eps,
147 final_eps: self.final_eps,
148 patch_dim: self.patch * self.patch * self.in_channels,
149 }
150 }
151
152 pub fn n_mod_blocks(&self) -> usize {
154 self.n_refiner + self.n_layers
155 }
156}
157
158#[derive(Clone, Copy, Debug, PartialEq, Eq)]
160pub struct ZShape {
161 pub h_lat: usize,
163 pub w_lat: usize,
164 pub grid: (usize, usize),
166 pub n_img: usize,
168 pub n_img_p: usize,
169 pub l: usize,
171 pub l_p: usize,
172}
173
174impl ZShape {
175 pub fn new(height: usize, width: usize, l: usize) -> Self {
177 let grid = (height / 16, width / 16);
178 let n_img = grid.0 * grid.1;
179 Self {
180 h_lat: height / 8,
181 w_lat: width / 8,
182 grid,
183 n_img,
184 n_img_p: ceil32(n_img),
185 l,
186 l_p: ceil32(l),
187 }
188 }
189
190 pub fn seq(&self) -> usize {
192 self.n_img_p + self.l_p
193 }
194}
195
196pub struct ZRope {
198 pub img: (Vec<f32>, Vec<f32>),
200 pub joint: (Vec<f32>, Vec<f32>),
202 pub cap: (Vec<f32>, Vec<f32>),
204}
205
206#[derive(Clone, Copy, Debug, PartialEq, Eq)]
208pub enum ZBlockId {
209 NoiseRefiner(usize),
210 ContextRefiner(usize),
211 Layer(usize),
212}
213
214pub struct ZBlockRefs<'a> {
217 pub noise_refiner: Vec<ZBlockRef<'a>>,
218 pub context_refiner: Vec<ZBlockRef<'a>>,
219 pub layers: Vec<ZBlockRef<'a>>,
220}
221
222pub struct ZPrepared {
225 pub key: u64,
226 pub shape: ZShape,
227 pub cap: Vec<f32>,
229 pub rope: ZRope,
230 pub device: bool,
231}
232
233struct ZBlock {
235 adaln: Option<(Vec<f32>, Vec<f32>)>,
238 norm1: Vec<f32>,
239 norm2: Vec<f32>,
240 ffn_norm1: Vec<f32>,
241 ffn_norm2: Vec<f32>,
242 norm_q: Vec<f32>,
243 norm_k: Vec<f32>,
244 q: Proj,
245 k: Proj,
246 v: Proj,
247 o: Proj,
248 w1: Proj,
249 w2: Proj,
250 w3: Proj,
251 idx: Option<[usize; 7]>,
253}
254
255pub struct ZImageDit {
258 pub cfg: ZConfig,
259 pub model: Option<Arc<CmfModel>>,
262 x_emb_w: Vec<f32>, x_emb_b: Vec<f32>,
264 x_pad: Vec<f32>, cap_pad: Vec<f32>, t_w0: Vec<f32>, t_b0: Vec<f32>,
268 t_w2: Vec<f32>, t_b2: Vec<f32>,
270 cap_norm: Vec<f32>, cap_w: Proj, cap_b: Vec<f32>,
273 final_mod_w: Vec<f32>, final_mod_b: Vec<f32>,
275 final_w: Vec<f32>, final_b: Vec<f32>,
277 noise_refiner: Vec<ZBlock>,
278 context_refiner: Vec<ZBlock>,
279 layers: Vec<ZBlock>,
280 pool: Option<Arc<Pool>>,
281}
282
283struct SendRows(*mut f32);
286unsafe impl Send for SendRows {}
287unsafe impl Sync for SendRows {}
288impl SendRows {
289 #[allow(clippy::mut_from_ref)]
291 unsafe fn row(&self, off: usize, len: usize) -> &mut [f32] {
292 unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
293 }
294 unsafe fn set(&self, off: usize, v: f32) {
295 unsafe { *self.0.add(off) = v }
296 }
297}
298
299fn pool_rows(pool: Option<&Pool>, n: usize, f: &(dyn Fn(usize, usize) + Sync)) {
300 match pool {
301 Some(p) => p.run_rows(n, f),
302 None => f(0, n),
303 }
304}
305
306fn silu(v: f32) -> f32 {
307 v / (1.0 + (-v).exp())
308}
309
310fn rms_norm_into(x: &[f32], w: &[f32], eps: f64, dst: &mut [f32]) {
312 let ss = x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / x.len() as f64;
313 let inv = 1.0 / (ss + eps).sqrt();
314 for ((d, &v), &g) in dst.iter_mut().zip(x).zip(w) {
315 *d = (v as f64 * inv) as f32 * g;
316 }
317}
318
319fn rms_norm_inplace(v: &mut [f32], w: &[f32], eps: f64) {
320 let ss = v.iter().map(|&x| (x as f64) * (x as f64)).sum::<f64>() / v.len() as f64;
321 let inv = 1.0 / (ss + eps).sqrt();
322 for (x, &g) in v.iter_mut().zip(w) {
323 *x = (*x as f64 * inv) as f32 * g;
324 }
325}
326
327fn linear_rows_multi(xs: &[Vec<f32>], w: &[f32], b: &[f32], pool: Option<&Pool>) -> Vec<Vec<f32>> {
331 let rows = b.len();
332 let n = xs.len();
333 let mut out = vec![0f32; n * rows];
334 let op = SendRows(out.as_mut_ptr());
335 pool_rows(pool, rows, &|lo, hi| {
336 for o in lo..hi {
337 for (si, x) in xs.iter().enumerate() {
338 let k = x.len();
339 let row = &w[o * k..(o + 1) * k];
340 let s: f64 = row.iter().zip(x).map(|(&a, &c)| a as f64 * c as f64).sum();
341 unsafe { op.set(si * rows + o, (s + b[o] as f64) as f32) };
343 }
344 }
345 });
346 out.chunks(rows.max(1)).map(|c| c.to_vec()).collect()
347}
348
349fn linear_row(x: &[f32], w: &[f32], b: &[f32], pool: Option<&Pool>) -> Vec<f32> {
350 let k = x.len();
351 let rows = b.len();
352 debug_assert_eq!(w.len(), rows * k);
353 let mut out = vec![0f32; rows];
354 let op = SendRows(out.as_mut_ptr());
355 pool_rows(pool, rows, &|lo, hi| {
356 for o in lo..hi {
357 let row = &w[o * k..(o + 1) * k];
358 let s: f64 = row
359 .iter()
360 .zip(x)
361 .map(|(&a, &c)| a as f64 * c as f64)
362 .sum();
363 unsafe { op.set(o, (s + b[o] as f64) as f32) };
365 }
366 });
367 out
368}
369
370fn softmax_inplace(row: &mut [f32]) {
372 let mx = row.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
373 let mut den = 0f64;
374 for r in row.iter_mut() {
375 *r = (*r - mx).exp();
376 den += *r as f64;
377 }
378 let inv = (1.0 / den) as f32;
379 for r in row.iter_mut() {
380 *r *= inv;
381 }
382}
383
384pub(crate) mod host_gemm {
394 use crate::pool::Pool;
395
396 const KC: usize = 256;
397 const MB: usize = 48;
398 const NB: usize = 48;
399
400 fn native() -> bool {
401 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
402 *ON.get_or_init(|| {
403 if matches!(std::env::var("CMF_ZIMAGE_HOST_GEMM").as_deref(), Ok("engine")) {
404 return false;
405 }
406 #[cfg(target_arch = "x86_64")]
407 {
408 std::arch::is_x86_feature_detected!("avx2")
409 && std::arch::is_x86_feature_detected!("fma")
410 }
411 #[cfg(not(target_arch = "x86_64"))]
412 {
413 false
414 }
415 })
416 }
417
418 struct YPtr(*mut f32);
419 unsafe impl Send for YPtr {}
420 unsafe impl Sync for YPtr {}
421
422 pub(crate) fn gemm_nt_ld(
425 x: &[f32],
426 w: &[f32],
427 y: &mut [f32],
428 ldy: usize,
429 n: usize,
430 k: usize,
431 m: usize,
432 pool: Option<&Pool>,
433 ) {
434 debug_assert!(x.len() >= n * k && w.len() >= m * k);
435 debug_assert!(n == 0 || y.len() >= (n - 1) * ldy + m);
436 #[cfg(target_arch = "x86_64")]
437 if native() && k % 8 == 0 {
438 for i in 0..n {
439 y[i * ldy..i * ldy + m].fill(0.0);
440 }
441 let (ti, tj) = (n.div_ceil(MB), m.div_ceil(NB));
442 let yp = YPtr(y.as_mut_ptr());
443 let work = |lo: usize, hi: usize| {
444 let yp = &yp;
445 for t in lo..hi {
446 let (bi, bj) = (t / tj, t % tj);
447 let (i0, i1) = (bi * MB, ((bi + 1) * MB).min(n));
448 let (j0, j1) = (bj * NB, ((bj + 1) * NB).min(m));
449 unsafe { tile(x, w, yp.0, ldy, k, i0, i1, j0, j1) };
451 }
452 };
453 match pool {
454 Some(p) => p.run_rows(ti * tj, &work),
455 None => work(0, ti * tj),
456 }
457 return;
458 }
459 if ldy == m {
460 crate::fcd_ops::gemm_nt(&x[..n * k], &w[..m * k], &mut y[..n * m], n, k, m, pool);
461 } else {
462 let mut tmp = vec![0f32; n * m];
463 crate::fcd_ops::gemm_nt(&x[..n * k], &w[..m * k], &mut tmp, n, k, m, pool);
464 for i in 0..n {
465 y[i * ldy..i * ldy + m].copy_from_slice(&tmp[i * m..(i + 1) * m]);
466 }
467 }
468 }
469
470 pub(crate) fn gemm_nt(
471 x: &[f32],
472 w: &[f32],
473 y: &mut [f32],
474 n: usize,
475 k: usize,
476 m: usize,
477 pool: Option<&Pool>,
478 ) {
479 gemm_nt_ld(x, w, y, m, n, k, m, pool)
480 }
481
482 #[cfg(target_arch = "x86_64")]
483 #[target_feature(enable = "avx2,fma")]
484 unsafe fn hsum(v: std::arch::x86_64::__m256) -> f32 {
485 use std::arch::x86_64::*;
486 let lo = _mm256_castps256_ps128(v);
487 let hi = _mm256_extractf128_ps(v, 1);
488 let s = _mm_add_ps(lo, hi);
489 let s = _mm_add_ps(s, _mm_movehl_ps(s, s));
490 let s = _mm_add_ss(s, _mm_shuffle_ps(s, s, 1));
491 _mm_cvtss_f32(s)
492 }
493
494 #[cfg(target_arch = "x86_64")]
495 #[target_feature(enable = "avx2,fma")]
496 #[allow(clippy::too_many_arguments)]
497 unsafe fn tile(
498 x: &[f32],
499 w: &[f32],
500 y: *mut f32,
501 ldy: usize,
502 k: usize,
503 i0: usize,
504 i1: usize,
505 j0: usize,
506 j1: usize,
507 ) {
508 use std::arch::x86_64::*;
509 let xp = x.as_ptr();
510 let wp = w.as_ptr();
511 let mut kb = 0;
512 while kb < k {
513 let kl = (k - kb).min(KC);
514 let mut i = i0;
515 while i < i1 {
516 let ih = (i1 - i).min(3);
517 let mut j = j0;
518 while j < j1 {
519 let jh = (j1 - j).min(3);
520 if ih == 3 && jh == 3 {
521 let (x0, x1, x2) = (
522 xp.add(i * k + kb),
523 xp.add((i + 1) * k + kb),
524 xp.add((i + 2) * k + kb),
525 );
526 let (w0, w1, w2) = (
527 wp.add(j * k + kb),
528 wp.add((j + 1) * k + kb),
529 wp.add((j + 2) * k + kb),
530 );
531 let mut a = [_mm256_setzero_ps(); 9];
532 let mut kk = 0;
533 while kk < kl {
534 let b0 = _mm256_loadu_ps(w0.add(kk));
535 let b1 = _mm256_loadu_ps(w1.add(kk));
536 let b2 = _mm256_loadu_ps(w2.add(kk));
537 let r0 = _mm256_loadu_ps(x0.add(kk));
538 a[0] = _mm256_fmadd_ps(r0, b0, a[0]);
539 a[1] = _mm256_fmadd_ps(r0, b1, a[1]);
540 a[2] = _mm256_fmadd_ps(r0, b2, a[2]);
541 let r1 = _mm256_loadu_ps(x1.add(kk));
542 a[3] = _mm256_fmadd_ps(r1, b0, a[3]);
543 a[4] = _mm256_fmadd_ps(r1, b1, a[4]);
544 a[5] = _mm256_fmadd_ps(r1, b2, a[5]);
545 let r2 = _mm256_loadu_ps(x2.add(kk));
546 a[6] = _mm256_fmadd_ps(r2, b0, a[6]);
547 a[7] = _mm256_fmadd_ps(r2, b1, a[7]);
548 a[8] = _mm256_fmadd_ps(r2, b2, a[8]);
549 kk += 8;
550 }
551 for r in 0..3 {
552 for c in 0..3 {
553 *y.add((i + r) * ldy + j + c) += hsum(a[r * 3 + c]);
554 }
555 }
556 } else {
557 for r in 0..ih {
558 for c in 0..jh {
559 let (xr, wr) = (xp.add((i + r) * k + kb), wp.add((j + c) * k + kb));
560 let mut acc = _mm256_setzero_ps();
561 let mut kk = 0;
562 while kk < kl {
563 acc = _mm256_fmadd_ps(
564 _mm256_loadu_ps(xr.add(kk)),
565 _mm256_loadu_ps(wr.add(kk)),
566 acc,
567 );
568 kk += 8;
569 }
570 *y.add((i + r) * ldy + j + c) += hsum(acc);
571 }
572 }
573 }
574 j += jh;
575 }
576 i += ih;
577 }
578 kb += kl;
579 }
580 }
581}
582
583fn lin(p: &Proj, x: &[f32], n: usize, y: &mut [f32], pool: Option<&Pool>) {
589 if matches!(std::env::var("CMF_ZIMAGE_HOST_GEMM").as_deref(), Ok("engine")) {
590 return p.matmat(x, n, y, pool);
591 }
592 let (rows, cols) = (p.rows(), p.cols());
593 match p {
594 Proj::F32 { w, .. } => host_gemm::gemm_nt(x, w, y, n, cols, rows, pool),
595 Proj::Q(q) => {
596 const CH: usize = 768;
597 let mut wbuf = vec![0f32; CH.min(rows) * cols];
598 let mut r0 = 0;
599 while r0 < rows {
600 let rc = CH.min(rows - r0);
601 {
602 let wp = SendRows(wbuf.as_mut_ptr());
603 pool_rows(pool, rc, &|lo, hi| {
604 for r in lo..hi {
605 q.row_f32(r0 + r, unsafe { wp.row(r * cols, cols) });
607 }
608 });
609 }
610 host_gemm::gemm_nt_ld(x, &wbuf[..rc * cols], &mut y[r0..], rows, n, cols, rc, pool);
611 r0 += rc;
612 }
613 }
614 }
615}
616
617fn cmf_f32(model: &CmfModel, name: &str) -> Result<Vec<f32>, String> {
619 crate::dit::cmf_f32(model, name)
620}
621
622pub fn ceil32(n: usize) -> usize {
624 n.div_ceil(SEQ_MULTI_OF) * SEQ_MULTI_OF
625}
626
627fn linspace_f32(start: f32, end: f32, n: usize) -> Vec<f32> {
630 if n == 1 {
631 return vec![start];
632 }
633 let step = (end - start) / (n - 1) as f32;
634 let half = n / 2;
635 (0..n)
636 .map(|i| {
637 if i < half {
638 start + step * i as f32
639 } else {
640 end - step * (n - 1 - i) as f32
641 }
642 })
643 .collect()
644}
645
646pub fn sigmas_torch_f32(n: usize, shift: f32) -> Vec<f32> {
650 let end = (1.0f64 / n as f64) as f32;
652 let mut s: Vec<f32> = linspace_f32(1.0, end, n)
653 .into_iter()
654 .map(|v| shift * v / (1.0 + (shift - 1.0) * v))
655 .collect();
656 s.push(0.0);
657 s
658}
659
660pub fn t_model(sigma: f32) -> f32 {
662 let t = sigma * 1000.0;
663 (1000.0 - t) / 1000.0
664}
665
666pub fn ids_and_rope(grid: (usize, usize), l: usize, theta: f64, axes: [usize; 3]) -> ZRope {
669 let l_p = ceil32(l);
670 let n_img = grid.0 * grid.1;
671 let n_img_p = ceil32(n_img);
672 let mut img_ids: Vec<[usize; 3]> = Vec::with_capacity(n_img_p);
673 for r in 0..grid.0 {
674 for c in 0..grid.1 {
675 img_ids.push([l_p + 1, r, c]);
676 }
677 }
678 img_ids.resize(n_img_p, [0, 0, 0]);
679 let cap_ids: Vec<[usize; 3]> = (0..l_p).map(|j| [1 + j, 0, 0]).collect();
680 let freqs: Vec<Vec<f64>> = axes
682 .iter()
683 .map(|&d| {
684 (0..d / 2)
685 .map(|j| 1.0 / theta.powf((2 * j) as f64 / d as f64))
686 .collect()
687 })
688 .collect();
689 let table = |ids: &[[usize; 3]]| -> (Vec<f32>, Vec<f32>) {
690 let pairs: usize = axes.iter().sum::<usize>() / 2;
691 let mut cos = Vec::with_capacity(ids.len() * pairs);
692 let mut sin = Vec::with_capacity(ids.len() * pairs);
693 for id in ids {
694 for (a, f) in freqs.iter().enumerate() {
695 for &fr in f {
696 let ang = (id[a] as f64 * fr) as f32;
697 cos.push(ang.cos());
698 sin.push(ang.sin());
699 }
700 }
701 }
702 (cos, sin)
703 };
704 let img = table(&img_ids);
705 let cap = table(&cap_ids);
706 let joint = (
707 [img.0.as_slice(), cap.0.as_slice()].concat(),
708 [img.1.as_slice(), cap.1.as_slice()].concat(),
709 );
710 ZRope { img, joint, cap }
711}
712
713pub fn patchify(latent: &[f32], c: usize, h: usize, w: usize) -> Vec<f32> {
715 let (hp, wp) = (h / 2, w / 2);
716 let pd = 4 * c;
717 let mut out = vec![0f32; hp * wp * pd];
718 for r in 0..hp {
719 for q in 0..wp {
720 let t = r * wp + q;
721 for dy in 0..2 {
722 for dx in 0..2 {
723 for ch in 0..c {
724 out[t * pd + (dy * 2 + dx) * c + ch] =
725 latent[(ch * h + 2 * r + dy) * w + 2 * q + dx];
726 }
727 }
728 }
729 }
730 }
731 out
732}
733
734pub fn unpatchify(tok: &[f32], c: usize, h: usize, w: usize) -> Vec<f32> {
736 let (hp, wp) = (h / 2, w / 2);
737 let pd = 4 * c;
738 let mut out = vec![0f32; c * h * w];
739 for r in 0..hp {
740 for q in 0..wp {
741 let t = r * wp + q;
742 for dy in 0..2 {
743 for dx in 0..2 {
744 for ch in 0..c {
745 out[(ch * h + 2 * r + dy) * w + 2 * q + dx] =
746 tok[t * pd + (dy * 2 + dx) * c + ch];
747 }
748 }
749 }
750 }
751 }
752 out
753}
754
755pub fn pad_rows_repeat_last(x: &[f32], rows: usize, rows_p: usize, width: usize) -> Vec<f32> {
757 let mut out = Vec::with_capacity(rows_p * width);
758 out.extend_from_slice(&x[..rows * width]);
759 for _ in rows..rows_p {
760 out.extend_from_within((rows - 1) * width..rows * width);
761 }
762 out
763}
764
765enum Src<'a> {
768 Cmf(&'a Arc<CmfModel>),
769 Dir(std::cell::RefCell<HashMap<String, crate::vae::StTensor>>),
770}
771
772impl Src<'_> {
773 fn f32(&self, name: &str) -> Result<Vec<f32>, String> {
774 match self {
775 Src::Cmf(m) => cmf_f32(m, &format!("dit.{name}")),
776 Src::Dir(t) => t
777 .borrow_mut()
778 .remove(name)
779 .map(|v| v.data)
780 .ok_or_else(|| format!("missing tensor {name}")),
781 }
782 }
783 fn proj(&self, name: &str) -> Result<(Proj, Option<usize>), String> {
784 match self {
785 Src::Cmf(m) => {
786 let full = format!("dit.{name}");
787 let idx = m.tensor_index(&full);
788 Ok((Proj::from_model(m, &full)?, idx))
789 }
790 Src::Dir(t) => {
791 let st = t
792 .borrow_mut()
793 .remove(name)
794 .ok_or_else(|| format!("missing tensor {name}"))?;
795 if st.shape.len() != 2 {
796 return Err(format!("{name}: expected 2-D"));
797 }
798 Ok((Proj::f32(st.data, st.shape[1]), None))
799 }
800 }
801 }
802}
803
804impl ZImageDit {
805 pub fn load_dir(dir: &Path) -> Result<Self, String> {
808 let cfg: serde_json::Value = serde_json::from_slice(
809 &std::fs::read(dir.join("config.json")).map_err(|e| format!("config.json: {e}"))?,
810 )
811 .map_err(|e| format!("config.json: {e}"))?;
812 let cfg = ZConfig::from_json(&cfg)?;
813 let idx: serde_json::Value = serde_json::from_slice(
814 &std::fs::read(dir.join("diffusion_pytorch_model.safetensors.index.json"))
815 .map_err(|e| format!("index: {e}"))?,
816 )
817 .map_err(|e| format!("index: {e}"))?;
818 let mut shards: Vec<String> = idx["weight_map"]
819 .as_object()
820 .ok_or("weight_map")?
821 .values()
822 .filter_map(|v| v.as_str().map(String::from))
823 .collect();
824 shards.sort();
825 shards.dedup();
826 let mut t = HashMap::new();
827 for sh in &shards {
828 t.extend(crate::vae::read_safetensors(&dir.join(sh))?);
829 }
830 Self::build(cfg, &Src::Dir(std::cell::RefCell::new(t)), None)
831 }
832
833 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
836 let cfg: serde_json::Value = serde_json::from_slice(
837 model
838 .tensor_bytes("dit.config_json")
839 .map_err(|e| e.to_string())?,
840 )
841 .map_err(|e| format!("dit.config_json: {e}"))?;
842 let cfg = ZConfig::from_json(&cfg)?;
843 Self::build(cfg, &Src::Cmf(model), Some(model.clone()))
844 }
845
846 fn build(cfg: ZConfig, src: &Src, model: Option<Arc<CmfModel>>) -> Result<Self, String> {
847 fn block(src: &Src, pfx: &str, modulated: bool) -> Result<ZBlock, String> {
848 let (q, iq) = src.proj(&format!("{pfx}.attention.to_q.weight"))?;
849 let (k, ik) = src.proj(&format!("{pfx}.attention.to_k.weight"))?;
850 let (v, iv) = src.proj(&format!("{pfx}.attention.to_v.weight"))?;
851 let (o, io) = src.proj(&format!("{pfx}.attention.to_out.0.weight"))?;
852 let (w1, i1) = src.proj(&format!("{pfx}.feed_forward.w1.weight"))?;
853 let (w2, i2) = src.proj(&format!("{pfx}.feed_forward.w2.weight"))?;
854 let (w3, i3) = src.proj(&format!("{pfx}.feed_forward.w3.weight"))?;
855 let idx = match (iq, ik, iv, io, i1, i3, i2) {
856 (Some(a), Some(b), Some(c), Some(d), Some(e), Some(f), Some(g)) => {
857 Some([a, b, c, d, e, f, g])
858 }
859 _ => None,
860 };
861 Ok(ZBlock {
862 adaln: if modulated {
863 Some((
864 src.f32(&format!("{pfx}.adaLN_modulation.0.weight"))?,
865 src.f32(&format!("{pfx}.adaLN_modulation.0.bias"))?,
866 ))
867 } else {
868 None
869 },
870 norm1: src.f32(&format!("{pfx}.attention_norm1.weight"))?,
871 norm2: src.f32(&format!("{pfx}.attention_norm2.weight"))?,
872 ffn_norm1: src.f32(&format!("{pfx}.ffn_norm1.weight"))?,
873 ffn_norm2: src.f32(&format!("{pfx}.ffn_norm2.weight"))?,
874 norm_q: src.f32(&format!("{pfx}.attention.norm_q.weight"))?,
875 norm_k: src.f32(&format!("{pfx}.attention.norm_k.weight"))?,
876 q,
877 k,
878 v,
879 o,
880 w1,
881 w2,
882 w3,
883 idx,
884 })
885 }
886 let blocks = |names: Vec<(String, bool)>| -> Result<Vec<ZBlock>, String> {
889 match src {
890 Src::Cmf(m) => std::thread::scope(|sc| {
891 let hs: Vec<_> = names
892 .iter()
893 .map(|(pfx, md)| sc.spawn(move || block(&Src::Cmf(m), pfx, *md)))
894 .collect();
895 hs.into_iter()
896 .map(|h| h.join().map_err(|_| "block loader panicked".to_string())?)
897 .collect()
898 }),
899 _ => names.iter().map(|(pfx, md)| block(src, pfx, *md)).collect(),
900 }
901 };
902 let noise_refiner = blocks((0..cfg.n_refiner).map(|i| (format!("noise_refiner.{i}"), true)).collect())?;
903 let context_refiner =
904 blocks((0..cfg.n_refiner).map(|i| (format!("context_refiner.{i}"), false)).collect())?;
905 let layers = blocks((0..cfg.n_layers).map(|i| (format!("layers.{i}"), true)).collect())?;
906 let pk = format!("{}-1", cfg.patch);
907 let (cap_w, _) = src.proj("cap_embedder.1.weight")?;
908 Ok(Self {
909 x_emb_w: src.f32(&format!("all_x_embedder.{pk}.weight"))?,
910 x_emb_b: src.f32(&format!("all_x_embedder.{pk}.bias"))?,
911 x_pad: src.f32("x_pad_token")?,
912 cap_pad: src.f32("cap_pad_token")?,
913 t_w0: src.f32("t_embedder.mlp.0.weight")?,
914 t_b0: src.f32("t_embedder.mlp.0.bias")?,
915 t_w2: src.f32("t_embedder.mlp.2.weight")?,
916 t_b2: src.f32("t_embedder.mlp.2.bias")?,
917 cap_norm: src.f32("cap_embedder.0.weight")?,
918 cap_w,
919 cap_b: src.f32("cap_embedder.1.bias")?,
920 final_mod_w: src.f32(&format!("all_final_layer.{pk}.adaLN_modulation.1.weight"))?,
921 final_mod_b: src.f32(&format!("all_final_layer.{pk}.adaLN_modulation.1.bias"))?,
922 final_w: src.f32(&format!("all_final_layer.{pk}.linear.weight"))?,
923 final_b: src.f32(&format!("all_final_layer.{pk}.linear.bias"))?,
924 noise_refiner,
925 context_refiner,
926 layers,
927 pool: Pool::from_env(),
928 cfg,
929 model,
930 })
931 }
932
933 pub fn geom(&self) -> ZGeom {
934 self.cfg.geom()
935 }
936
937 fn pool(&self) -> Option<&Pool> {
938 self.pool.as_deref()
939 }
940
941 fn blk(&self, id: ZBlockId) -> &ZBlock {
942 match id {
943 ZBlockId::NoiseRefiner(i) => &self.noise_refiner[i],
944 ZBlockId::ContextRefiner(i) => &self.context_refiner[i],
945 ZBlockId::Layer(i) => &self.layers[i],
946 }
947 }
948
949 pub fn temb(&self, t_model: f32) -> Vec<f32> {
952 const HALF: usize = 128;
953 let t = t_model * self.cfg.t_scale;
954 let c = -(10000f64.ln()) as f32;
956 let mut freq = vec![0f32; 2 * HALF];
957 for i in 0..HALF {
958 let f = (c * i as f32 / HALF as f32).exp();
959 let arg = t * f;
960 freq[i] = arg.cos();
961 freq[HALF + i] = arg.sin();
962 }
963 let mut h = linear_row(&freq, &self.t_w0, &self.t_b0, None);
964 for v in h.iter_mut() {
965 *v = silu(*v);
966 }
967 linear_row(&h, &self.t_w2, &self.t_b2, None)
968 }
969
970 pub fn mods_for_steps(&self, t_models: &[f32]) -> Vec<f32> {
974 let per = self.cfg.n_mod_blocks() * 4 * self.cfg.dim;
975 let mut out = vec![0f32; t_models.len() * per];
976 let tembs: Vec<Vec<f32>> = t_models.iter().map(|&t| self.temb(t)).collect();
981 let mut off = 0;
982 for b in self.noise_refiner.iter().chain(&self.layers) {
983 let (w, bias) = b.adaln.as_ref().expect("modulated block");
984 let rows = bias.len();
985 let ys = linear_rows_multi(&tembs, w, bias, self.pool());
986 for (si, y) in ys.iter().enumerate() {
987 out[si * per + off..si * per + off + rows].copy_from_slice(y);
988 }
989 off += rows;
990 }
991 out
992 }
993
994 pub fn final_scale_for_steps(&self, t_models: &[f32]) -> Vec<f32> {
997 let mut out = Vec::with_capacity(t_models.len() * self.cfg.dim);
998 for &t in t_models {
999 let te: Vec<f32> = self.temb(t).into_iter().map(silu).collect();
1000 let m = linear_row(&te, &self.final_mod_w, &self.final_mod_b, self.pool());
1001 out.extend(m.into_iter().map(|v| 1.0 + v));
1002 }
1003 out
1004 }
1005
1006 pub fn embed_caption(&self, cap_feats: &[f32], l: usize) -> Vec<f32> {
1010 let (cf, dim) = (self.cfg.cap_feat_dim, self.cfg.dim);
1011 let l_p = ceil32(l);
1012 let mut xn = vec![0f32; l * cf];
1015 for (o, src) in xn.chunks_exact_mut(cf).zip(cap_feats.chunks_exact(cf)) {
1016 rms_norm_into(src, &self.cap_norm, self.cfg.norm_eps as f64, o);
1017 }
1018 let mut out = vec![0f32; l_p * dim];
1019 lin(&self.cap_w, &xn, l, &mut out[..l * dim], self.pool());
1020 for row in out[..l * dim].chunks_exact_mut(dim) {
1021 for (v, &b) in row.iter_mut().zip(&self.cap_b) {
1022 *v += b;
1023 }
1024 }
1025 for row in out[l * dim..].chunks_exact_mut(dim) {
1026 row.copy_from_slice(&self.cap_pad);
1027 }
1028 out
1029 }
1030
1031 pub fn refine_caption_cpu(&self, cap: &mut [f32], rope_cap: (&[f32], &[f32])) {
1034 let n = cap.len() / self.cfg.dim;
1035 for i in 0..self.context_refiner.len() {
1036 self.block_cpu(ZBlockId::ContextRefiner(i), cap, n, rope_cap, None);
1037 }
1038 }
1039
1040 fn attention(&self, q_all: &[f32], k_all: &[f32], v_all: &[f32], n: usize, attn: &mut [f32]) {
1043 let (nh, nkv, hd) = (self.cfg.n_heads, self.cfg.n_kv_heads, self.cfg.head_dim);
1044 let hpk = nh / nkv;
1045 let pool = self.pool();
1046 let scale = 1.0 / (hd as f32).sqrt();
1047 let mut qh = vec![0f32; n * hd];
1048 let mut kh = vec![0f32; n * hd];
1049 let mut vt = vec![0f32; hd * n];
1050 let mut scores = vec![0f32; n * n];
1051 let mut oh = vec![0f32; n * hd];
1052 for hh in 0..nh {
1053 let kv = hh / hpk;
1054 {
1055 let (sq, sk, sv) = (
1056 SendRows(qh.as_mut_ptr()),
1057 SendRows(kh.as_mut_ptr()),
1058 SendRows(vt.as_mut_ptr()),
1059 );
1060 pool_rows(pool, n, &|lo, hi| {
1061 for p in lo..hi {
1062 let qsrc = &q_all[(p * nh + hh) * hd..(p * nh + hh + 1) * hd];
1063 let qd = unsafe { sq.row(p * hd, hd) };
1065 for (d, &v) in qsrc.iter().enumerate() {
1066 qd[d] = v * scale;
1067 }
1068 unsafe { sk.row(p * hd, hd) }.copy_from_slice(
1069 &k_all[(p * nkv + kv) * hd..(p * nkv + kv + 1) * hd],
1070 );
1071 let vv = &v_all[(p * nkv + kv) * hd..(p * nkv + kv + 1) * hd];
1072 for (d, &val) in vv.iter().enumerate() {
1073 unsafe { sv.set(d * n + p, val) };
1074 }
1075 }
1076 });
1077 }
1078 host_gemm::gemm_nt(&qh, &kh, &mut scores, n, hd, n, pool);
1079 {
1080 let sp = SendRows(scores.as_mut_ptr());
1081 pool_rows(pool, n, &|lo, hi| {
1082 for r in lo..hi {
1083 softmax_inplace(unsafe { sp.row(r * n, n) });
1085 }
1086 });
1087 }
1088 host_gemm::gemm_nt(&scores, &vt, &mut oh, n, n, hd, pool);
1089 let sa = SendRows(attn.as_mut_ptr());
1090 pool_rows(pool, n, &|lo, hi| {
1091 for p in lo..hi {
1092 unsafe { sa.row((p * nh + hh) * hd, hd) }
1094 .copy_from_slice(&oh[p * hd..(p + 1) * hd]);
1095 }
1096 });
1097 }
1098 }
1099
1100 pub fn block_cpu(
1103 &self,
1104 blk: ZBlockId,
1105 x: &mut [f32],
1106 n: usize,
1107 rope: (&[f32], &[f32]),
1108 m: Option<&[f32]>,
1109 ) {
1110 let b = self.blk(blk);
1111 let (hs, nh, nkv, hd) = (
1112 self.cfg.dim,
1113 self.cfg.n_heads,
1114 self.cfg.n_kv_heads,
1115 self.cfg.head_dim,
1116 );
1117 let eps = self.cfg.norm_eps as f64;
1118 let pool = self.pool();
1119 let (s_msa, g_msa, s_mlp, g_mlp) = match m {
1120 Some(m) => (
1121 Some(&m[..hs]),
1122 Some(m[hs..2 * hs].iter().map(|v| v.tanh()).collect::<Vec<f32>>()),
1123 Some(&m[2 * hs..3 * hs]),
1124 Some(m[3 * hs..4 * hs].iter().map(|v| v.tanh()).collect::<Vec<f32>>()),
1125 ),
1126 None => (None, None, None, None),
1127 };
1128 let norm_scaled = |src: &[f32], w: &[f32], s: Option<&[f32]>, dst: &mut [f32]| {
1130 let sr = SendRows(dst.as_mut_ptr());
1131 pool_rows(pool, n, &|lo, hi| {
1132 for p in lo..hi {
1133 let row = unsafe { sr.row(p * hs, hs) };
1135 rms_norm_into(&src[p * hs..(p + 1) * hs], w, eps, row);
1136 if let Some(s) = s {
1137 for (r, &sc) in row.iter_mut().zip(s) {
1138 *r *= 1.0 + sc;
1139 }
1140 }
1141 }
1142 });
1143 };
1144 let residual = |src: &[f32], w: &[f32], gate: Option<&[f32]>, x: &mut [f32]| {
1146 let sr = SendRows(x.as_mut_ptr());
1147 pool_rows(pool, n, &|lo, hi| {
1148 let mut tmp = vec![0f32; hs];
1149 for p in lo..hi {
1150 rms_norm_into(&src[p * hs..(p + 1) * hs], w, eps, &mut tmp);
1151 let dst = unsafe { sr.row(p * hs, hs) };
1153 match gate {
1154 Some(g) => {
1155 for ((d, &v), >) in dst.iter_mut().zip(&tmp).zip(g) {
1156 *d += gt * v;
1157 }
1158 }
1159 None => {
1160 for (d, &v) in dst.iter_mut().zip(&tmp) {
1161 *d += v;
1162 }
1163 }
1164 }
1165 }
1166 });
1167 };
1168 let mut xn = vec![0f32; n * hs];
1170 norm_scaled(x, &b.norm1, s_msa, &mut xn);
1171 let mut q_all = vec![0f32; n * nh * hd];
1172 let mut k_all = vec![0f32; n * nkv * hd];
1173 let mut v_all = vec![0f32; n * nkv * hd];
1174 lin(&b.q, &xn, n, &mut q_all, pool);
1175 lin(&b.k, &xn, n, &mut k_all, pool);
1176 lin(&b.v, &xn, n, &mut v_all, pool);
1177 let (cos, sin) = rope;
1178 let pairs = hd / 2;
1179 for (all, heads, w) in [(&mut q_all, nh, &b.norm_q), (&mut k_all, nkv, &b.norm_k)] {
1180 let sr = SendRows(all.as_mut_ptr());
1181 pool_rows(pool, n, &|lo, hi| {
1182 for p in lo..hi {
1183 for h in 0..heads {
1184 let v = unsafe { sr.row((p * heads + h) * hd, hd) };
1186 rms_norm_inplace(v, w, eps);
1187 for j in 0..pairs {
1188 let (c, s) = (cos[p * pairs + j], sin[p * pairs + j]);
1189 let (a, bb) = (v[2 * j], v[2 * j + 1]);
1190 v[2 * j] = a * c - bb * s;
1191 v[2 * j + 1] = a * s + bb * c;
1192 }
1193 }
1194 }
1195 });
1196 }
1197 let mut attn = vec![0f32; n * nh * hd];
1198 self.attention(&q_all, &k_all, &v_all, n, &mut attn);
1199 drop((q_all, k_all, v_all));
1200 let mut proj = vec![0f32; n * hs];
1201 lin(&b.o, &attn, n, &mut proj, pool);
1202 drop(attn);
1203 residual(&proj, &b.norm2, g_msa.as_deref(), x);
1204 norm_scaled(x, &b.ffn_norm1, s_mlp, &mut xn);
1206 let inter = b.w1.rows();
1207 let mut g_all = vec![0f32; n * inter];
1208 let mut u_all = vec![0f32; n * inter];
1209 lin(&b.w1, &xn, n, &mut g_all, pool);
1210 lin(&b.w3, &xn, n, &mut u_all, pool);
1211 {
1212 let sg = SendRows(g_all.as_mut_ptr());
1213 pool_rows(pool, n, &|lo, hi| {
1214 for p in lo..hi {
1215 let g = unsafe { sg.row(p * inter, inter) };
1217 for (gv, &uv) in g.iter_mut().zip(&u_all[p * inter..(p + 1) * inter]) {
1218 *gv = silu(*gv) * uv;
1219 }
1220 }
1221 });
1222 }
1223 drop(u_all);
1224 lin(&b.w2, &g_all, n, &mut proj, pool);
1225 residual(&proj, &b.ffn_norm2, g_mlp.as_deref(), x);
1226 }
1227
1228 pub fn block_refs(&self) -> Option<ZBlockRefs<'_>> {
1230 fn r(b: &ZBlock) -> Option<ZBlockRef<'_>> {
1231 let [wq, wk, wv, wo, w1, w3, w2] = b.idx?;
1232 Some(ZBlockRef {
1233 wq,
1234 wk,
1235 wv,
1236 wo,
1237 w1,
1238 w3,
1239 w2,
1240 norm1: &b.norm1,
1241 norm2: &b.norm2,
1242 ffn_norm1: &b.ffn_norm1,
1243 ffn_norm2: &b.ffn_norm2,
1244 norm_q: &b.norm_q,
1245 norm_k: &b.norm_k,
1246 })
1247 }
1248 self.model.as_ref()?;
1249 Some(ZBlockRefs {
1250 noise_refiner: self.noise_refiner.iter().map(r).collect::<Option<_>>()?,
1251 context_refiner: self.context_refiner.iter().map(r).collect::<Option<_>>()?,
1252 layers: self.layers.iter().map(r).collect::<Option<_>>()?,
1253 })
1254 }
1255
1256 pub fn prepare(
1261 &self,
1262 cap_feats: &[f32],
1263 shape: ZShape,
1264 key: u64,
1265 mods_all: Option<(&[f32], &[f32])>,
1266 ) -> Result<ZPrepared, String> {
1267 self.prepare_with(cap_feats, shape, key, mods_all, gpu_allowed())
1268 }
1269
1270 pub fn prepare_with(
1274 &self,
1275 cap_feats: &[f32],
1276 shape: ZShape,
1277 key: u64,
1278 mods_all: Option<(&[f32], &[f32])>,
1279 device: bool,
1280 ) -> Result<ZPrepared, String> {
1281 let mut p = self.prepare_host(cap_feats, shape, key, device)?;
1282 if device {
1283 self.attach_device(&mut p, mods_all);
1284 }
1285 Ok(p)
1286 }
1287
1288 pub fn prepare_host(
1292 &self,
1293 cap_feats: &[f32],
1294 shape: ZShape,
1295 key: u64,
1296 device_refine: bool,
1297 ) -> Result<ZPrepared, String> {
1298 if cap_feats.len() != shape.l * self.cfg.cap_feat_dim || shape.l == 0 {
1299 return Err(format!(
1300 "caption features: {} floats for {} tokens of {}",
1301 cap_feats.len(),
1302 shape.l,
1303 self.cfg.cap_feat_dim
1304 ));
1305 }
1306 let rope = ids_and_rope(shape.grid, shape.l, self.cfg.rope_theta, self.cfg.axes_dims);
1307 let mut cap = self.embed_caption(cap_feats, shape.l);
1308 let refs = if device_refine { self.block_refs() } else { None };
1309 let geom = self.geom();
1310 let dev_refined = match (&refs, &self.model) {
1311 (Some(r), Some(m)) => crate::gpu::zimage_refine_caption(
1312 m,
1313 &geom,
1314 &r.context_refiner,
1315 (&rope.cap.0, &rope.cap.1),
1316 &mut cap,
1317 ),
1318 _ => false,
1319 };
1320 if !dev_refined {
1321 self.refine_caption_cpu(&mut cap, (&rope.cap.0, &rope.cap.1));
1322 }
1323 Ok(ZPrepared {
1324 key,
1325 shape,
1326 cap,
1327 rope,
1328 device: false,
1329 })
1330 }
1331
1332 fn prepare_args<'a>(
1333 &'a self,
1334 m: &'a Arc<CmfModel>,
1335 r: &'a ZBlockRefs<'a>,
1336 p: &'a ZPrepared,
1337 key: u64,
1338 mods_all: Option<(&'a [f32], &'a [f32])>,
1339 neg: Option<&'a ZPrepared>,
1340 ) -> crate::gpu::ZPrepareArgs<'a> {
1341 let shape = p.shape;
1342 crate::gpu::ZPrepareArgs {
1343 model: m,
1344 geom: self.geom(),
1345 key,
1346 n_img: shape.n_img,
1347 n_img_p: shape.n_img_p,
1348 n_cap_p: shape.l_p,
1349 grid: shape.grid,
1350 cap: &p.cap,
1351 rope_img: (&p.rope.img.0, &p.rope.img.1),
1352 rope_joint: (&p.rope.joint.0, &p.rope.joint.1),
1353 x_emb_w: &self.x_emb_w,
1354 x_emb_b: &self.x_emb_b,
1355 x_pad: &self.x_pad,
1356 final_w: &self.final_w,
1357 final_b: &self.final_b,
1358 noise_refiner: &r.noise_refiner,
1359 layers: &r.layers,
1360 mods_all: mods_all.map(|m| m.0),
1361 final_scale_all: mods_all.map(|m| m.1),
1362 neg: neg.map(|n| crate::gpu::ZNegArgs {
1363 cap: &n.cap,
1364 n_cap_p: n.shape.l_p,
1365 rope_img: (&n.rope.img.0, &n.rope.img.1),
1366 rope_joint: (&n.rope.joint.0, &n.rope.joint.1),
1367 }),
1368 }
1369 }
1370
1371 pub fn preload_device(&self) -> bool {
1374 match (self.block_refs(), &self.model) {
1375 (Some(r), Some(m)) => crate::gpu::zimage_preload(
1376 m,
1377 &self.geom(),
1378 &r.noise_refiner,
1379 &r.layers,
1380 &r.context_refiner,
1381 ),
1382 _ => false,
1383 }
1384 }
1385
1386 pub fn attach_device(&self, p: &mut ZPrepared, mods_all: Option<(&[f32], &[f32])>) -> bool {
1388 let refs = self.block_refs();
1389 p.device = match (&refs, &self.model) {
1390 (Some(r), Some(m)) => {
1391 crate::gpu::zimage_prepare(&self.prepare_args(m, r, p, p.key, mods_all, None))
1392 }
1393 _ => false,
1394 };
1395 p.device
1396 }
1397
1398 pub fn attach_device_pair(
1402 &self,
1403 pos: &ZPrepared,
1404 neg: &ZPrepared,
1405 key: u64,
1406 mods_all: Option<(&[f32], &[f32])>,
1407 ) -> bool {
1408 if pos.shape.grid != neg.shape.grid {
1409 return false;
1410 }
1411 let refs = self.block_refs();
1412 match (&refs, &self.model) {
1413 (Some(r), Some(m)) => {
1414 crate::gpu::zimage_prepare(&self.prepare_args(m, r, pos, key, mods_all, Some(neg)))
1415 }
1416 _ => false,
1417 }
1418 }
1419
1420 pub fn step_pair_device(
1424 &self,
1425 key: u64,
1426 n_img: usize,
1427 step: usize,
1428 x_tok: &[f32],
1429 mods: &[f32],
1430 final_scale: &[f32],
1431 ) -> Option<(Vec<f32>, Vec<f32>)> {
1432 let pd = self.geom().patch_dim;
1433 let mut out = vec![0f32; n_img * pd];
1434 let mut out_neg = vec![0f32; n_img * pd];
1435 crate::gpu::zimage_step(&mut crate::gpu::ZStepArgs {
1436 key,
1437 step,
1438 x_tok,
1439 mods,
1440 final_scale,
1441 out: &mut out,
1442 out_neg: Some(&mut out_neg),
1443 })
1444 .then_some((out, out_neg))
1445 }
1446
1447 pub fn step(
1452 &self,
1453 p: &ZPrepared,
1454 step: usize,
1455 x_tok: &[f32],
1456 mods: &[f32],
1457 final_scale: &[f32],
1458 ) -> Vec<f32> {
1459 if p.device {
1460 let mut out = vec![0f32; p.shape.n_img * self.geom().patch_dim];
1461 if crate::gpu::zimage_step(&mut crate::gpu::ZStepArgs {
1462 key: p.key,
1463 step,
1464 x_tok,
1465 mods,
1466 final_scale,
1467 out: &mut out,
1468 out_neg: None,
1469 }) {
1470 return out;
1471 }
1472 }
1473 self.step_cpu(p, x_tok, mods, final_scale)
1474 }
1475
1476 pub fn tokens(&self, latent: &[f32], shape: &ZShape) -> Vec<f32> {
1479 pad_rows_repeat_last(
1480 &patchify(latent, self.cfg.in_channels, shape.h_lat, shape.w_lat),
1481 shape.n_img,
1482 shape.n_img_p,
1483 self.geom().patch_dim,
1484 )
1485 }
1486
1487 pub fn embed_image(&self, x_tok: &[f32], n_img: usize, n_img_p: usize) -> Vec<f32> {
1490 let (dim, pd) = (self.cfg.dim, self.geom().patch_dim);
1491 let mut x = vec![0f32; n_img_p * dim];
1492 host_gemm::gemm_nt(
1493 &x_tok[..n_img * pd],
1494 &self.x_emb_w,
1495 &mut x[..n_img * dim],
1496 n_img,
1497 pd,
1498 dim,
1499 self.pool(),
1500 );
1501 for row in x[..n_img * dim].chunks_exact_mut(dim) {
1502 for (v, &b) in row.iter_mut().zip(&self.x_emb_b) {
1503 *v += b;
1504 }
1505 }
1506 for row in x[n_img * dim..].chunks_exact_mut(dim) {
1507 row.copy_from_slice(&self.x_pad);
1508 }
1509 x
1510 }
1511
1512 pub fn final_layer(&self, u: &[f32], n: usize, final_scale: &[f32]) -> Vec<f32> {
1515 let (dim, pd) = (self.cfg.dim, self.geom().patch_dim);
1516 let eps = self.cfg.final_eps as f64;
1517 let mut y = vec![0f32; n * dim];
1518 {
1519 let sy = SendRows(y.as_mut_ptr());
1520 pool_rows(self.pool(), n, &|lo, hi| {
1521 for p in lo..hi {
1522 let x = &u[p * dim..(p + 1) * dim];
1523 let mean = x.iter().map(|&v| v as f64).sum::<f64>() / dim as f64;
1524 let var =
1525 x.iter().map(|&v| (v as f64 - mean).powi(2)).sum::<f64>() / dim as f64;
1526 let inv = 1.0 / (var + eps).sqrt();
1527 let d = unsafe { sy.row(p * dim, dim) };
1529 for ((o, &v), &s) in d.iter_mut().zip(x).zip(final_scale) {
1530 *o = ((v as f64 - mean) * inv) as f32 * s;
1531 }
1532 }
1533 });
1534 }
1535 let mut out = vec![0f32; n * pd];
1536 host_gemm::gemm_nt(&y, &self.final_w, &mut out, n, dim, pd, self.pool());
1537 for row in out.chunks_exact_mut(pd) {
1538 for (v, &b) in row.iter_mut().zip(&self.final_b) {
1539 *v += b;
1540 }
1541 }
1542 out
1543 }
1544
1545 pub fn step_cpu(&self, p: &ZPrepared, x_tok: &[f32], mods: &[f32], final_scale: &[f32]) -> Vec<f32> {
1550 self.step_cpu_taps(p, x_tok, mods, final_scale, &mut |_, _| {})
1551 }
1552
1553 pub fn step_cpu_taps(
1556 &self,
1557 p: &ZPrepared,
1558 x_tok: &[f32],
1559 mods: &[f32],
1560 final_scale: &[f32],
1561 tap: &mut dyn FnMut(&str, &[f32]),
1562 ) -> Vec<f32> {
1563 let s = p.shape;
1564 let dim = self.cfg.dim;
1565 let md = 4 * dim;
1566 let mut x = self.embed_image(x_tok, s.n_img, s.n_img_p);
1567 tap("x_seq", &x);
1568 for i in 0..self.noise_refiner.len() {
1569 let m = &mods[i * md..(i + 1) * md];
1570 self.block_cpu(
1571 ZBlockId::NoiseRefiner(i),
1572 &mut x,
1573 s.n_img_p,
1574 (&p.rope.img.0, &p.rope.img.1),
1575 Some(m),
1576 );
1577 tap(&format!("nr{i}_out"), &x);
1578 }
1579 x.extend_from_slice(&p.cap);
1580 let n = s.seq();
1581 tap("u_in", &x);
1582 let nr = self.noise_refiner.len();
1583 for i in 0..self.layers.len() {
1584 let m = &mods[(nr + i) * md..(nr + i + 1) * md];
1585 self.block_cpu(
1586 ZBlockId::Layer(i),
1587 &mut x,
1588 n,
1589 (&p.rope.joint.0, &p.rope.joint.1),
1590 Some(m),
1591 );
1592 tap(&format!("l{i}_out"), &x);
1593 }
1594 let out = self.final_layer(&x, s.n_img, final_scale);
1595 tap("final_out", &out);
1596 out
1597 }
1598}
1599
1600pub fn gpu_allowed() -> bool {
1604 !matches!(std::env::var("CMF_ZIMAGE_GPU").as_deref(), Ok("0"))
1605 && !matches!(std::env::var("CMF_GPU").as_deref(), Ok("0"))
1606}
1607
1608#[cfg(test)]
1609mod tests {
1610 use super::*;
1611
1612 #[test]
1613 fn sigmas_match_the_spec_table() {
1614 let s = sigmas_torch_f32(8, 3.0);
1615 let spec = [
1616 1.0f32,
1617 0.954545438,
1618 0.899999976,
1619 0.833333313,
1620 0.75,
1621 0.642857134,
1622 0.5,
1623 0.300000012,
1624 0.0,
1625 ];
1626 assert_eq!(s.len(), 9);
1627 for (a, b) in s.iter().zip(spec) {
1628 assert!((a - b).abs() <= 1e-7, "{s:?}");
1629 }
1630 let s4 = sigmas_torch_f32(4, 3.0);
1631 for (a, b) in s4.iter().zip([1.0f32, 0.9, 0.75, 0.5, 0.0]) {
1632 assert!((a - b).abs() <= 1e-6, "{s4:?}");
1633 }
1634 assert_eq!(t_model(1.0), 0.0);
1635 assert_eq!(t_model(0.5), 0.5);
1636 }
1637
1638 #[test]
1639 fn patchify_roundtrip_and_order() {
1640 let (c, h, w) = (16, 6, 4);
1641 let lat: Vec<f32> = (0..c * h * w).map(|i| i as f32).collect();
1642 let t = patchify(&lat, c, h, w);
1643 assert_eq!(t[(1 * 2) * 64 + (1 * 2) * 16 + 3], lat[(3 * h + 3) * w]);
1645 assert_eq!(unpatchify(&t, c, h, w), lat);
1646 }
1647
1648 #[test]
1649 fn ids_and_pads() {
1650 assert_eq!(ceil32(22), 32);
1651 assert_eq!(ceil32(32), 32);
1652 assert_eq!(ceil32(33), 64);
1653 let r = ids_and_rope((2, 3), 5, 256.0, [32, 48, 48]);
1654 assert_eq!(r.img.0.len(), 32 * 64);
1656 assert_eq!(r.cap.0.len(), 32 * 64);
1657 assert_eq!(r.joint.0.len(), 64 * 64);
1658 assert!(r.img.0[31 * 64..].iter().all(|&c| c == 1.0));
1660 assert!((r.cap.0[0] - 1f32.cos()).abs() < 1e-7);
1662 assert!((r.img.0[0] - 33f32.cos()).abs() < 1e-6);
1664 let x = [1.0f32, 2.0, 3.0, 4.0];
1665 assert_eq!(
1666 pad_rows_repeat_last(&x, 2, 4, 2),
1667 vec![1.0, 2.0, 3.0, 4.0, 3.0, 4.0, 3.0, 4.0]
1668 );
1669 }
1670}