1use crate::dit::Proj;
38use crate::pool::Pool;
39use cortiq_core::CmfModel;
40use std::sync::Arc;
41
42const FRAME_PER_TOKEN: [f64; 5] = [1.0, 4.0, 4.0, 4.0, 4.0];
45const FRAME_RESCALE: f64 = 5.0 / 3.0;
46
47const TAG_VIDEO: usize = 0;
49const TAG_TEXT: usize = 1;
50const TAG_AUDIO: usize = 2;
51const MODALITIES: usize = 3;
52const EXPAND: usize = 6;
54const VISUAL_COND_TIMESTEP: f64 = 0.999;
56const AUDIO_COND_TIMESTEP: f64 = 1.0;
58
59pub fn time_shift_sigma(sigma: f64, from_shift: f64, to_shift: f64) -> f64 {
63 let base = sigma / (from_shift + sigma * (1.0 - from_shift));
64 to_shift * base / (1.0 + (to_shift - 1.0) * base)
65}
66
67pub fn time_shift_slope(sigma: f64, from_shift: f64, to_shift: f64) -> f64 {
69 let base = sigma / (from_shift + sigma * (1.0 - from_shift));
70 (to_shift * (1.0 + (from_shift - 1.0) * base).powi(2))
71 / (from_shift * (1.0 + (to_shift - 1.0) * base).powi(2))
72}
73
74#[derive(Clone, Copy, PartialEq, Debug)]
77pub enum Kind {
78 Text,
79 Cond,
81 RefImg,
83 RefAudio,
85 Audio,
86 Video,
87}
88
89#[derive(Clone, Copy, Debug)]
91pub struct Segment {
92 pub start: usize,
93 pub stop: usize,
94 pub kind: Kind,
95}
96
97pub struct Layout {
100 pub seq_len: usize,
101 pub segments: Vec<Segment>,
102 pub pos: Vec<[f64; 3]>,
104 pub text_len: usize,
105 pub audio_t: usize,
106 pub latent_t: usize,
107 pub lat_h: usize,
108 pub lat_w: usize,
109 pub frame_rows: usize,
111 pub text_tags: Vec<u8>,
114}
115
116#[derive(Clone, Debug)]
118pub enum Ref {
119 Image { lat_h: usize, lat_w: usize },
121 Audio { t: usize },
123 Video {
126 latent_t: usize,
127 lat_h: usize,
128 lat_w: usize,
129 audio_t: usize,
130 },
131}
132
133fn push_audio_grid(pos: &mut Vec<[f64; 3]>, cursor: f64, t: usize, w_low: f64, w_high: f64) {
137 for ch in 0..2 {
138 let w = if ch == 0 { w_low } else { w_high };
139 for i in 0..t {
140 pos.push([cursor + i as f64, 0.0, w]);
141 }
142 }
143}
144
145fn axis_from_sqrt_area(dim: usize, patch: usize, sqrt_area: f64) -> Vec<f64> {
147 let ratio = dim as f64 / sqrt_area;
148 let n = dim / patch;
149 (0..n)
150 .map(|i| (i as f64 * (ratio / n as f64) + (1.0 - ratio) / 2.0) * 32.0)
151 .collect()
152}
153
154impl Layout {
155 pub fn t2va(text_len: usize, latent_t: usize, lat_h: usize, lat_w: usize, audio_t: usize) -> Self {
159 Self::build(text_len, latent_t, lat_h, lat_w, audio_t, &[], &[])
160 }
161
162 pub fn fl2va(
172 text_len: usize,
173 latent_t: usize,
174 lat_h: usize,
175 lat_w: usize,
176 audio_t: usize,
177 frames: &[(usize, usize)],
178 text_tags: &[u8],
179 ) -> Self {
180 Self::build(text_len, latent_t, lat_h, lat_w, audio_t, frames, text_tags)
181 }
182
183 pub fn ref2va(
188 text_len: usize,
189 latent_t: usize,
190 lat_h: usize,
191 lat_w: usize,
192 audio_t: usize,
193 refs: &[Ref],
194 text_tags: &[u8],
195 ) -> Self {
196 Self::build_full(text_len, latent_t, lat_h, lat_w, audio_t, &[], refs, text_tags)
197 }
198
199 fn build(
200 text_len: usize,
201 latent_t: usize,
202 lat_h: usize,
203 lat_w: usize,
204 audio_t: usize,
205 frames: &[(usize, usize)],
206 text_tags: &[u8],
207 ) -> Self {
208 Self::build_full(text_len, latent_t, lat_h, lat_w, audio_t, frames, &[], text_tags)
209 }
210
211 #[allow(clippy::too_many_arguments)]
212 fn build_full(
213 text_len: usize,
214 latent_t: usize,
215 lat_h: usize,
216 lat_w: usize,
217 audio_t: usize,
218 frames: &[(usize, usize)],
219 refs: &[Ref],
220 text_tags: &[u8],
221 ) -> Self {
222 let area = ((lat_h * lat_w) as f64).sqrt();
223 let h_axis = axis_from_sqrt_area(lat_h, 2, area);
224 let w_axis = axis_from_sqrt_area(lat_w, 2, area);
225 let frame_rows = h_axis.len() * w_axis.len();
226
227 let mut pos: Vec<[f64; 3]> = Vec::new();
228 let mut segments = Vec::new();
229
230 segments.push(Segment { start: 0, stop: text_len, kind: Kind::Text });
231 for i in 0..text_len {
232 pos.push([i as f64, 0.0, 0.0]);
233 }
234
235 let mut cursor = text_len as f64;
239 let (w_low_t, w_high_t) = (w_axis[0], w_axis[w_axis.len() - 1]);
240
241 for r in refs {
242 match r {
243 Ref::Image { lat_h, lat_w } => {
244 let (rh, rw) = (
245 axis_from_sqrt_area(*lat_h, 2, ((lat_h * lat_w) as f64).sqrt()),
246 axis_from_sqrt_area(*lat_w, 2, ((lat_h * lat_w) as f64).sqrt()),
247 );
248 let start = pos.len();
249 for &h in &rh {
250 for &w in &rw {
251 pos.push([cursor, h, w]);
252 }
253 }
254 segments.push(Segment { start, stop: pos.len(), kind: Kind::RefImg });
255 cursor += 1.0;
256 }
257 Ref::Audio { t } => {
258 if *t > 0 {
259 let start = pos.len();
260 push_audio_grid(&mut pos, cursor, *t, w_low_t, w_high_t);
261 segments.push(Segment { start, stop: pos.len(), kind: Kind::RefAudio });
262 }
263 cursor += *t as f64;
264 }
265 Ref::Video { latent_t: vt, lat_h: rh_, lat_w: rw_, audio_t: rt } => {
266 let area = ((rh_ * rw_) as f64).sqrt();
267 let rh = axis_from_sqrt_area(*rh_, 2, area);
268 let rw = axis_from_sqrt_area(*rw_, 2, area);
269 if *rt > 0 {
273 let start = pos.len();
274 push_audio_grid(&mut pos, cursor, *rt, rw[0], rw[rw.len() - 1]);
275 segments.push(Segment { start, stop: pos.len(), kind: Kind::RefAudio });
276 }
277 let start = pos.len();
278 let mut t_coord = cursor;
279 for k in 0..*vt {
280 for &h in &rh {
281 for &w in &rw {
282 pos.push([t_coord, h, w]);
283 }
284 }
285 t_coord += FRAME_RESCALE * FRAME_PER_TOKEN[k % 5];
286 }
287 segments.push(Segment { start, stop: pos.len(), kind: Kind::RefImg });
288 let spans: f64 = (0..*vt)
289 .map(|k| FRAME_RESCALE * FRAME_PER_TOKEN[k % 5])
290 .sum();
291 cursor += (*rt as f64).max(spans);
292 }
293 }
294 }
295 let cursor = cursor;
296
297 let spans: f64 = (0..latent_t)
299 .map(|k| FRAME_RESCALE * FRAME_PER_TOKEN[k % 5])
300 .sum();
301 for &(pixel_index, frame_count) in frames {
302 let cond_t = if pixel_index == 0 {
303 cursor
304 } else if frame_count > 0 && pixel_index == frame_count - 1 {
305 cursor + spans - FRAME_RESCALE
306 } else {
307 panic!("only the first and last frame can anchor a keyframe");
308 };
309 let start = pos.len();
310 for &h in &h_axis {
311 for &w in &w_axis {
312 pos.push([cond_t, h, w]);
313 }
314 }
315 segments.push(Segment { start, stop: pos.len(), kind: Kind::Cond });
316 }
317
318 let a_start = pos.len();
322 push_audio_grid(&mut pos, cursor, audio_t, w_low_t, w_high_t);
323 segments.push(Segment { start: a_start, stop: pos.len(), kind: Kind::Audio });
324
325 let v_start = pos.len();
328 let mut t_coord = cursor;
329 for k in 0..latent_t {
330 for &h in &h_axis {
331 for &w in &w_axis {
332 pos.push([t_coord, h, w]);
333 }
334 }
335 t_coord += FRAME_RESCALE * FRAME_PER_TOKEN[k % 5];
336 }
337 segments.push(Segment { start: v_start, stop: pos.len(), kind: Kind::Video });
338
339 Self {
340 seq_len: pos.len(),
341 segments,
342 pos,
343 text_len,
344 audio_t,
345 latent_t,
346 lat_h,
347 lat_w,
348 frame_rows,
349 text_tags: if text_tags.is_empty() {
350 vec![TAG_TEXT as u8; text_len]
351 } else {
352 text_tags.to_vec()
353 },
354 }
355 }
356
357 pub fn cond_rows(&self) -> usize {
359 self.segments
360 .iter()
361 .filter(|s| s.kind == Kind::Cond)
362 .map(|s| s.stop - s.start)
363 .sum()
364 }
365
366 fn segment(&self, kind: Kind) -> Segment {
367 *self
368 .segments
369 .iter()
370 .find(|s| s.kind == kind)
371 .expect("every layout carries all three streams")
372 }
373}
374
375struct Adaln {
378 w: Proj,
380 b: Vec<f32>,
381 table: Vec<f32>,
383 rank: usize,
384 out: usize,
385}
386
387impl Adaln {
388 fn load(model: &Arc<CmfModel>, prefix: &str) -> Result<Self, String> {
389 let w = Proj::from_model(model, &format!("{prefix}.weight"))?;
390 let table = crate::dit::cmf_f32(model, &format!("{prefix}.table"))?;
391 let b = crate::dit::cmf_f32(model, &format!("{prefix}.bias"))?;
392 let out = w.rows();
393 let rank = table.len() / CURVE_GRID;
394 Ok(Self { w, b, table, rank, out })
395 }
396
397 fn eval(&self, ts: &[f64], pool: Option<&Pool>) -> Vec<f32> {
401 let g = CURVE_GRID;
402 let mut coords = vec![0f32; ts.len() * self.rank];
403 for (i, &t) in ts.iter().enumerate() {
404 let p = (t.clamp(0.0, 1.0) * (g - 1) as f64) as f32;
408 let i0 = (p.floor() as usize).min(g - 2);
409 let f = p - i0 as f32;
410 for k in 0..self.rank {
411 let (a, b) = (self.table[i0 * self.rank + k], self.table[(i0 + 1) * self.rank + k]);
412 coords[i * self.rank + k] = a + (b - a) * f;
413 }
414 }
415 let mut out = vec![0f32; ts.len() * self.out];
416 self.w.matmat(&coords, ts.len(), &mut out, pool);
417 for row in out.chunks_exact_mut(self.out) {
418 for (v, &bv) in row.iter_mut().zip(&self.b) {
419 *v += bv;
420 }
421 }
422 out
423 }
424}
425
426struct Block {
427 norm1: Vec<f32>,
428 norm2: Vec<f32>,
429 qkv: Proj, out: Proj, q_norm: Vec<f32>,
432 k_norm: Vec<f32>,
433 fc1: Proj, fc2: Proj, adaln: Option<Adaln>,
436}
437
438pub(crate) const CURVE_GRID: usize = 1025;
439
440pub struct MiniMaxH3 {
441 video_patch: Proj,
442 video_patch_b: Vec<f32>,
443 audio_patch: Proj,
444 audio_patch_b: Vec<f32>,
445 condition: Proj,
446 condition_b: Vec<f32>,
447 refiner: Vec<Block>,
448 refiner_norm: Vec<f32>,
449 blocks: Vec<Block>,
450 final_norm: Vec<f32>,
451 final_adaln: Adaln,
452 video_out: Proj,
453 video_out_b: Vec<f32>,
454 audio_out: Proj,
455 audio_out_b: Vec<f32>,
456 inv_freq: Vec<f32>,
457 pool: Option<Arc<Pool>>,
458 pub hidden: usize,
459 pub heads: usize,
460 pub head_dim: usize,
461 pub ffn: usize,
462 pub latents_dim: usize,
463 pub audio_dim: usize,
464 pub text_dim: usize,
465 pub shift_video: f64,
466 pub shift_audio: f64,
467 eps: f64,
468 qk_eps: f64,
469 final_eps: f64,
470 pub cond_aug: f64,
474 pub cond_aug_audio: f64,
477}
478
479fn rms_norm_into(x: &[f32], w: &[f32], eps: f64, dst: &mut [f32]) {
480 let ss = x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / x.len() as f64;
481 let inv = 1.0 / (ss + eps).sqrt();
482 for ((d, &v), &g) in dst.iter_mut().zip(x).zip(w) {
483 *d = (v as f64 * inv) as f32 * g;
484 }
485}
486
487fn silu(v: f32) -> f32 {
488 v / (1.0 + (-v).exp())
489}
490
491impl MiniMaxH3 {
492 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
493 let cfg: serde_json::Value = serde_json::from_slice(
494 model.tensor_bytes("dit.config_json").map_err(|e| e.to_string())?,
495 )
496 .map_err(|e| format!("dit.config_json: {e}"))?;
497 let u = |k: &str| cfg[k].as_u64().unwrap_or(0) as usize;
498 let f = |k: &str, d: f64| cfg[k].as_f64().unwrap_or(d);
499 let n_blocks = u("num_layers");
500 let n_refiner = u("token_refiner_num_layers");
501 let f32v = |n: &str| crate::dit::cmf_f32(model, n);
502
503 let load_block = |prefix: &str, with_adaln: bool| -> Result<Block, String> {
504 Ok(Block {
505 norm1: f32v(&format!("dit.{prefix}.norm1.weight"))?,
506 norm2: f32v(&format!("dit.{prefix}.norm2.weight"))?,
507 qkv: Proj::from_model(model, &format!("dit.{prefix}.attn.qkv_proj.weight"))?,
508 out: Proj::from_model(model, &format!("dit.{prefix}.attn.out_proj.weight"))?,
509 q_norm: f32v(&format!("dit.{prefix}.attn.q_norm.weight"))?,
510 k_norm: f32v(&format!("dit.{prefix}.attn.k_norm.weight"))?,
511 fc1: Proj::from_model(model, &format!("dit.{prefix}.mlp.fc1.weight"))?,
512 fc2: Proj::from_model(model, &format!("dit.{prefix}.mlp.fc2.weight"))?,
513 adaln: if with_adaln {
514 Some(Adaln::load(model, &format!("dit.{prefix}.adaln"))?)
515 } else {
516 None
517 },
518 })
519 };
520 let blocks = (0..n_blocks)
521 .map(|i| load_block(&format!("blocks.{i}"), true))
522 .collect::<Result<Vec<_>, _>>()?;
523 let refiner = (0..n_refiner)
524 .map(|i| load_block(&format!("token_refiner.blocks.{i}"), false))
525 .collect::<Result<Vec<_>, _>>()?;
526
527 Ok(Self {
528 video_patch: Proj::from_model(model, "dit.video_patch_proj.weight")?,
529 video_patch_b: f32v("dit.video_patch_proj.bias")?,
530 audio_patch: Proj::from_model(model, "dit.audio_patch_proj.weight")?,
531 audio_patch_b: f32v("dit.audio_patch_proj.bias")?,
532 condition: Proj::from_model(model, "dit.condition_proj.weight")?,
533 condition_b: f32v("dit.condition_proj.bias")?,
534 refiner,
535 refiner_norm: f32v("dit.token_refiner.final_norm.weight")?,
536 blocks,
537 final_norm: f32v("dit.final_layer.norm.weight")?,
538 final_adaln: Adaln::load(model, "dit.final_layer.adaln")?,
539 video_out: Proj::from_model(model, "dit.final_layer.video_out.weight")?,
540 video_out_b: f32v("dit.final_layer.video_out.bias")?,
541 audio_out: Proj::from_model(model, "dit.final_layer.audio_out.weight")?,
542 audio_out_b: f32v("dit.final_layer.audio_out.bias")?,
543 inv_freq: f32v("dit.rope_inv_freq")?,
544 pool: Pool::from_env(),
545 hidden: u("hidden_size"),
546 heads: u("num_attention_heads"),
547 head_dim: u("attention_head_dim"),
548 ffn: u("ffn_hidden_size"),
549 latents_dim: u("latents_dim"),
550 audio_dim: u("audio_latents_dim"),
551 text_dim: u("text_dim"),
552 shift_video: f("sigma_shift_video", 12.0),
553 shift_audio: f("sigma_shift_audio", 3.0),
554 eps: f("norm_eps", 1e-5),
555 qk_eps: f("qk_norm_eps", 1e-5),
556 final_eps: f("final_norm_eps", 1e-5),
557 cond_aug: VISUAL_COND_TIMESTEP,
558 cond_aug_audio: AUDIO_COND_TIMESTEP,
559 })
560 }
561
562 pub fn refine_text(&self, states: &[f32], n: usize) -> Vec<f32> {
566 let pool = self.pool.as_deref();
567 let mut h = vec![0f32; n * self.hidden];
568 self.condition.matmat(states, n, &mut h, pool);
569 for row in h.chunks_exact_mut(self.hidden) {
570 for (v, &b) in row.iter_mut().zip(&self.condition_b) {
571 *v += b;
572 }
573 }
574 let ids: Vec<[f64; 3]> = Vec::new();
575 for blk in &self.refiner {
576 self.block_forward(blk, &mut h, n, None, &ids, &[]);
577 }
578 let mut out = vec![0f32; n * self.hidden];
579 for (o, x) in out.chunks_exact_mut(self.hidden).zip(h.chunks_exact(self.hidden)) {
580 rms_norm_into(x, &self.refiner_norm, self.final_eps, o);
581 }
582 out
583 }
584
585 fn rope_angles(&self, pos: &[[f64; 3]]) -> Vec<f32> {
589 let k = self.inv_freq.len();
590 let mut out = Vec::with_capacity(pos.len() * 3 * k);
591 for p in pos {
592 for axis in 0..3 {
593 for j in 0..k {
594 out.push((p[axis] * self.inv_freq[j] as f64) as f32);
595 }
596 }
597 }
598 out
599 }
600
601 #[allow(clippy::too_many_arguments)]
612 fn norm_rope_w(
613 &self,
614 v: &mut [f32],
615 n: usize,
616 heads: usize,
617 w: &[f32],
618 angles: &[f32],
619 stride: usize,
620 off: usize,
621 ) {
622 let hd = self.head_dim;
623 let pairs = if angles.is_empty() { 0 } else { angles.len() / n };
624 let pool = self.pool.as_deref();
625 let ptr = SendPtr(v.as_mut_ptr());
626 let work = |lo: usize, hi: usize| {
627 for p in lo..hi {
628 for h in 0..heads {
629 let x = unsafe { ptr.row(p * stride + off + h * hd, hd) };
632 let ss = x.iter().map(|&a| (a as f64) * (a as f64)).sum::<f64>() / hd as f64;
633 let inv = 1.0 / (ss + self.qk_eps).sqrt();
634 for (d, &g) in x.iter_mut().zip(w) {
635 *d = (*d as f64 * inv) as f32 * g;
636 }
637 for j in 0..pairs {
638 let a = angles[p * pairs + j];
639 let (s, c) = a.sin_cos();
640 let (lo_v, hi_v) = (x[j], x[j + pairs]);
641 x[j] = lo_v * c - hi_v * s;
642 x[j + pairs] = lo_v * s + hi_v * c;
643 }
644 }
645 }
646 };
647 match pool {
648 Some(pl) => pl.run_rows(n, &work),
649 None => work(0, n),
650 }
651 }
652
653 fn attention(&self, qkv: &[f32], n: usize, attn: &mut [f32]) {
655 let (nh, hd) = (self.heads, self.head_dim);
656 let inner = nh * hd;
657 let scale = 1.0 / (hd as f32).sqrt();
658 let pool = self.pool.as_deref();
659 let mut qh = vec![0f32; n * hd];
660 let mut kh = vec![0f32; n * hd];
661 let mut vt = vec![0f32; hd * n];
662 let mut scores = vec![0f32; n * n];
663 let mut oh = vec![0f32; n * hd];
664 for h in 0..nh {
665 for p in 0..n {
666 let base = p * 3 * inner;
667 let qs = &qkv[base + h * hd..base + (h + 1) * hd];
668 for (d, &val) in qs.iter().enumerate() {
669 qh[p * hd + d] = val * scale;
670 }
671 kh[p * hd..(p + 1) * hd]
672 .copy_from_slice(&qkv[base + inner + h * hd..base + inner + (h + 1) * hd]);
673 let vs = &qkv[base + 2 * inner + h * hd..base + 2 * inner + (h + 1) * hd];
674 for (d, &val) in vs.iter().enumerate() {
675 vt[d * n + p] = val;
676 }
677 }
678 crate::fcd_ops::gemm_nt(&qh, &kh, &mut scores, n, hd, n, pool);
679 let sp = SendPtr(scores.as_mut_ptr());
680 let soft = |lo: usize, hi: usize| {
681 for r in lo..hi {
682 softmax_inplace(unsafe { sp.row(r * n, n) });
684 }
685 };
686 match pool {
687 Some(pl) => pl.run_rows(n, &soft),
688 None => soft(0, n),
689 }
690 crate::fcd_ops::gemm_nt(&scores, &vt, &mut oh, n, n, hd, pool);
691 for p in 0..n {
692 attn[p * inner + h * hd..p * inner + (h + 1) * hd]
693 .copy_from_slice(&oh[p * hd..(p + 1) * hd]);
694 }
695 }
696 }
697
698 fn block_forward(
702 &self,
703 blk: &Block,
704 x: &mut [f32],
705 n: usize,
706 mods: Option<&[f32]>,
707 pos: &[[f64; 3]],
708 rows: &[u32],
709 ) {
710 let hs = self.hidden;
711 let pool = self.pool.as_deref();
712 let inner = self.heads * self.head_dim;
713 let angles = if pos.is_empty() {
714 Vec::new()
715 } else {
716 self.rope_angles(pos)
717 };
718
719 let mut xn = vec![0f32; n * hs];
720 self.norm_rows(&mut xn, x, n, hs, &blk.norm1, self.eps);
721 if let (Some(m), false) = (mods, rows.is_empty()) {
722 self.modulate(&mut xn, hs, m, rows, 0, 1);
723 }
724 let mut qkv = vec![0f32; n * 3 * inner];
725 blk.qkv.matmat(&xn, n, &mut qkv, pool);
726 for (which, w) in [(0usize, &blk.q_norm), (1usize, &blk.k_norm)] {
729 self.norm_rope_w(&mut qkv, n, self.heads, w, &angles, 3 * inner, which * inner);
730 }
731 let mut attn = vec![0f32; n * inner];
732 self.attention(&qkv, n, &mut attn);
733 let mut proj = vec![0f32; n * hs];
734 blk.out.matmat(&attn, n, &mut proj, pool);
735 self.residual(x, hs, &proj, mods, rows, 2);
736
737 self.norm_rows(&mut xn, x, n, hs, &blk.norm2, self.eps);
738 if let (Some(m), false) = (mods, rows.is_empty()) {
739 self.modulate(&mut xn, hs, m, rows, 3, 4);
740 }
741 let mut gu = vec![0f32; n * 2 * self.ffn];
742 blk.fc1.matmat(&xn, n, &mut gu, pool);
743 let ffn = self.ffn;
745 let mut act = vec![0f32; n * ffn];
746 let ap = SendPtr(act.as_mut_ptr());
747 pool_rows(pool, n, &|lo, hi| {
748 for p in lo..hi {
749 let row = &gu[p * 2 * ffn..(p + 1) * 2 * ffn];
750 let (g, up) = row.split_at(ffn);
751 for (o, (&a, &b)) in unsafe { ap.row(p * ffn, ffn) }
753 .iter_mut()
754 .zip(g.iter().zip(up))
755 {
756 *o = silu(a) * b;
757 }
758 }
759 });
760 blk.fc2.matmat(&act, n, &mut proj, pool);
761 self.residual(x, hs, &proj, mods, rows, 5);
762 }
763
764 fn norm_rows(&self, dst: &mut [f32], src: &[f32], n: usize, hs: usize, w: &[f32], eps: f64) {
769 let ptr = SendPtr(dst.as_mut_ptr());
770 pool_rows(self.pool.as_deref(), n, &|lo, hi| {
771 for p in lo..hi {
772 rms_norm_into(&src[p * hs..(p + 1) * hs], w, eps, unsafe {
774 ptr.row(p * hs, hs)
775 });
776 }
777 });
778 }
779
780 fn modulate(
782 &self,
783 x: &mut [f32],
784 hs: usize,
785 mods: &[f32],
786 rows: &[u32],
787 shift_e: usize,
788 scale_e: usize,
789 ) {
790 let stride = EXPAND * hs;
791 let ptr = SendPtr(x.as_mut_ptr());
792 pool_rows(self.pool.as_deref(), rows.len(), &|lo, hi| {
793 for p in lo..hi {
794 let base = rows[p] as usize * stride;
795 let shift = &mods[base + shift_e * hs..base + (shift_e + 1) * hs];
796 let scale = &mods[base + scale_e * hs..base + (scale_e + 1) * hs];
797 for ((v, &sc), &sh) in unsafe { ptr.row(p * hs, hs) }
799 .iter_mut()
800 .zip(scale)
801 .zip(shift)
802 {
803 *v = *v * (1.0 + sc) + sh;
804 }
805 }
806 });
807 }
808
809 fn residual(
812 &self,
813 x: &mut [f32],
814 hs: usize,
815 other: &[f32],
816 mods: Option<&[f32]>,
817 rows: &[u32],
818 gate_e: usize,
819 ) {
820 let n = x.len() / hs;
821 let stride = EXPAND * hs;
822 let ptr = SendPtr(x.as_mut_ptr());
823 let gated = mods.filter(|_| !rows.is_empty());
824 pool_rows(self.pool.as_deref(), n, &|lo, hi| {
825 for p in lo..hi {
826 let row = unsafe { ptr.row(p * hs, hs) };
828 let src = &other[p * hs..(p + 1) * hs];
829 match gated {
830 Some(m) => {
831 let base = rows[p] as usize * stride;
832 let gate = &m[base + gate_e * hs..base + (gate_e + 1) * hs];
833 for ((v, &g), &o) in row.iter_mut().zip(gate).zip(src) {
834 *v += g * o;
835 }
836 }
837 None => {
838 for (v, &o) in row.iter_mut().zip(src) {
839 *v += o;
840 }
841 }
842 }
843 }
844 });
845 }
846
847 pub fn forward(
854 &self,
855 layout: &Layout,
856 text: &[f32],
857 video: &[f32],
858 audio: &[f32],
859 sigma_v: f64,
860 cond: &[Vec<f32>],
861 ) -> (Vec<f32>, Vec<f32>) {
862 let hs = self.hidden;
863 let pool = self.pool.as_deref();
864 let sigma_v = sigma_v.max(1e-6);
865 let t_v = 1.0 - sigma_v;
866 let t_a = 1.0 - time_shift_sigma(sigma_v, self.shift_video, self.shift_audio);
867
868 let has_cond = layout
873 .segments
874 .iter()
875 .any(|s| matches!(s.kind, Kind::Cond | Kind::RefImg));
876 let has_ref_audio = layout.segments.iter().any(|s| s.kind == Kind::RefAudio);
877 let t_cond_a = t_a.max(self.cond_aug_audio);
880 let t_cond = t_v.max(self.cond_aug);
885 let mut ts = vec![t_v, t_a];
886 if has_cond {
887 ts.push(t_cond);
888 }
889 if has_ref_audio {
890 ts.push(t_cond_a);
891 }
892 ts.sort_by(|a, b| a.partial_cmp(b).unwrap());
893 ts.dedup();
894 let row_of = |t: f64| ts.iter().position(|&x| x == t).unwrap();
895 let (row_v, row_a) = (row_of(t_v), row_of(t_a));
896 let row_c = if has_cond { row_of(t_cond) } else { 0 };
897 let row_ca = if has_ref_audio { row_of(t_cond_a) } else { 0 };
898
899 let mut rows = vec![0u32; layout.seq_len];
903 for s in &layout.segments {
904 match s.kind {
905 Kind::Text => {
906 for (i, v) in rows[s.start..s.stop].iter_mut().enumerate() {
907 let tag = *layout.text_tags.get(i).unwrap_or(&(TAG_TEXT as u8));
908 *v = (row_v * MODALITIES + tag as usize) as u32;
909 }
910 }
911 Kind::Cond | Kind::RefImg => {
915 for v in rows[s.start..s.stop].iter_mut() {
916 *v = (row_c * MODALITIES + TAG_VIDEO) as u32;
917 }
918 }
919 Kind::RefAudio => {
920 for v in rows[s.start..s.stop].iter_mut() {
921 *v = (row_ca * MODALITIES + TAG_AUDIO) as u32;
922 }
923 }
924 Kind::Video => {
925 for v in rows[s.start..s.stop].iter_mut() {
926 *v = (row_v * MODALITIES + TAG_VIDEO) as u32;
927 }
928 }
929 Kind::Audio => {
930 for v in rows[s.start..s.stop].iter_mut() {
931 *v = (row_a * MODALITIES + TAG_AUDIO) as u32;
932 }
933 }
934 }
935 }
936
937 let v_rows = patchify_video(video, self.latents_dim, layout.latent_t, layout.lat_h, layout.lat_w);
939 let v_n = v_rows.len() / (self.latents_dim * 4);
940 let a_rows = pack_audio(audio, self.audio_dim, layout.audio_t);
941 let vd = self.latents_dim * 4;
944 let cond_rows: Vec<Vec<f32>> = cond
945 .iter()
946 .enumerate()
947 .map(|(i, z)| {
948 let mut r = patchify_video(z, self.latents_dim, 1, layout.lat_h, layout.lat_w);
949 if self.cond_aug < 1.0 {
950 let noise = crate::videogen::gauss_pub(r.len(), 0x5EED ^ i as u64);
955 let a = self.cond_aug as f32;
956 for (v, n) in r.iter_mut().zip(&noise) {
957 *v = a * *v + (1.0 - a) * n;
958 }
959 }
960 r
961 })
962 .collect();
963
964 let mut h = vec![0f32; layout.seq_len * hs];
965 let mut ci = 0usize;
966 for s in layout.segments.iter().filter(|s| s.kind == Kind::Cond) {
967 let n = s.stop - s.start;
968 let r = cond_rows
969 .get(ci)
970 .unwrap_or_else(|| panic!("layout has {} cond segments, {} latents given", ci + 1, cond_rows.len()));
971 self.video_patch
972 .matmat(r, n, &mut h[s.start * hs..s.stop * hs], pool);
973 for row in h[s.start * hs..s.stop * hs].chunks_exact_mut(hs) {
974 for (v, &b) in row.iter_mut().zip(&self.video_patch_b) {
975 *v += b;
976 }
977 }
978 ci += 1;
979 }
980 let vseg = layout.segment(Kind::Video);
981 let aseg = layout.segment(Kind::Audio);
982 let tseg = layout.segment(Kind::Text);
983 h[tseg.start * hs..tseg.stop * hs].copy_from_slice(&text[..(tseg.stop - tseg.start) * hs]);
984 self.video_patch.matmat(&v_rows, v_n, &mut h[vseg.start * hs..vseg.stop * hs], pool);
985 self.audio_patch.matmat(
986 &a_rows,
987 aseg.stop - aseg.start,
988 &mut h[aseg.start * hs..aseg.stop * hs],
989 pool,
990 );
991 for row in h[vseg.start * hs..vseg.stop * hs].chunks_exact_mut(hs) {
992 for (v, &b) in row.iter_mut().zip(&self.video_patch_b) {
993 *v += b;
994 }
995 }
996 for row in h[aseg.start * hs..aseg.stop * hs].chunks_exact_mut(hs) {
997 for (v, &b) in row.iter_mut().zip(&self.audio_patch_b) {
998 *v += b;
999 }
1000 }
1001
1002 for (i, blk) in self.blocks.iter().enumerate() {
1004 let mods = blk.adaln.as_ref().unwrap().eval(&ts, pool);
1005 self.block_forward(blk, &mut h, layout.seq_len, Some(&mods), &layout.pos, &rows);
1006 if std::env::var_os("CMF_DIT_PROGRESS").is_some() {
1007 eprint!("\r block {}/{}", i + 1, self.blocks.len());
1008 }
1009 }
1010
1011 let fm = self.final_adaln.eval(&ts, pool);
1013 let mut video_out = vec![0f32; (vseg.stop - vseg.start) * vd];
1014 let mut audio_out = vec![0f32; (aseg.stop - aseg.start) * self.audio_dim];
1015 for (seg, row, w, b, dst, dim) in [
1016 (vseg, row_v, &self.video_out, &self.video_out_b, &mut video_out, vd),
1017 (aseg, row_a, &self.audio_out, &self.audio_out_b, &mut audio_out, self.audio_dim),
1018 ] {
1019 let n = seg.stop - seg.start;
1020 let mut hn = vec![0f32; n * hs];
1021 for (o, src) in hn.chunks_exact_mut(hs).zip(h[seg.start * hs..seg.stop * hs].chunks_exact(hs)) {
1022 rms_norm_into(src, &self.final_norm, self.final_eps, o);
1023 }
1024 let shift = &fm[row * 2 * hs..row * 2 * hs + hs];
1027 let scale = &fm[row * 2 * hs + hs..(row + 1) * 2 * hs];
1028 for r in hn.chunks_exact_mut(hs) {
1029 for ((v, &sc), &sh) in r.iter_mut().zip(scale).zip(shift) {
1030 *v = *v * (1.0 + sc) + sh;
1031 }
1032 }
1033 w.matmat(&hn, n, dst, pool);
1034 for r in dst.chunks_exact_mut(dim) {
1035 for (v, &bv) in r.iter_mut().zip(b.iter()) {
1036 *v += bv;
1037 }
1038 }
1039 }
1040
1041 let video = unpatchify_video(
1045 &video_out,
1046 self.latents_dim,
1047 layout.latent_t,
1048 layout.lat_h,
1049 layout.lat_w,
1050 );
1051 let audio = unpack_audio(&audio_out, self.audio_dim, layout.audio_t);
1052 (
1053 video.iter().map(|&v| -v).collect(),
1054 audio.iter().map(|&v| -v).collect(),
1055 )
1056 }
1057}
1058
1059fn pool_rows(pool: Option<&Pool>, n: usize, f: &(dyn Fn(usize, usize) + Sync)) {
1063 match pool {
1064 Some(p) => p.run_rows(n, f),
1065 None => f(0, n),
1066 }
1067}
1068
1069pub fn patchify_video(x: &[f32], c: usize, t: usize, h: usize, w: usize) -> Vec<f32> {
1074 let (ph, pw) = (h / 2, w / 2);
1075 let mut out = vec![0f32; t * ph * pw * c * 4];
1076 let mut i = 0;
1077 for ti in 0..t {
1078 for hi in 0..ph {
1079 for wi in 0..pw {
1080 for ci in 0..c {
1081 for p in 0..2 {
1082 for q in 0..2 {
1083 out[i] = x[((ci * t + ti) * h + hi * 2 + p) * w + wi * 2 + q];
1084 i += 1;
1085 }
1086 }
1087 }
1088 }
1089 }
1090 }
1091 out
1092}
1093
1094pub fn unpatchify_video(rows: &[f32], c: usize, t: usize, h: usize, w: usize) -> Vec<f32> {
1096 let (ph, pw) = (h / 2, w / 2);
1097 let mut out = vec![0f32; c * t * h * w];
1098 let mut i = 0;
1099 for ti in 0..t {
1100 for hi in 0..ph {
1101 for wi in 0..pw {
1102 for ci in 0..c {
1103 for p in 0..2 {
1104 for q in 0..2 {
1105 out[((ci * t + ti) * h + hi * 2 + p) * w + wi * 2 + q] = rows[i];
1106 i += 1;
1107 }
1108 }
1109 }
1110 }
1111 }
1112 }
1113 out
1114}
1115
1116pub fn pack_audio(x: &[f32], c: usize, t: usize) -> Vec<f32> {
1119 let mut out = vec![0f32; 2 * t * c];
1120 for ch in 0..2 {
1121 for ti in 0..t {
1122 for ci in 0..c {
1123 out[(ch * t + ti) * c + ci] = x[(ci * 2 + ch) * t + ti];
1124 }
1125 }
1126 }
1127 out
1128}
1129
1130pub fn unpack_audio(rows: &[f32], c: usize, t: usize) -> Vec<f32> {
1132 let mut out = vec![0f32; c * 2 * t];
1133 for ch in 0..2 {
1134 for ti in 0..t {
1135 for ci in 0..c {
1136 out[(ci * 2 + ch) * t + ti] = rows[(ch * t + ti) * c + ci];
1137 }
1138 }
1139 }
1140 out
1141}
1142
1143struct SendPtr(*mut f32);
1146unsafe impl Send for SendPtr {}
1147unsafe impl Sync for SendPtr {}
1148impl SendPtr {
1149 #[allow(clippy::mut_from_ref)]
1151 unsafe fn row(&self, off: usize, len: usize) -> &mut [f32] {
1152 unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
1153 }
1154}
1155
1156fn softmax_inplace(row: &mut [f32]) {
1157 let mx = row.iter().cloned().fold(f32::MIN, f32::max);
1158 let mut den = 0f32;
1159 for r in row.iter_mut() {
1160 *r = (*r - mx).exp();
1161 den += *r;
1162 }
1163 if den > 0.0 {
1164 let inv = 1.0 / den;
1165 for r in row.iter_mut() {
1166 *r *= inv;
1167 }
1168 }
1169}
1170
1171#[cfg(test)]
1172mod tests {
1173 use super::*;
1174
1175 #[test]
1176 fn schedule_remap_is_an_involution() {
1177 for &s in &[1e-3, 0.25, 0.5, 0.8, 0.972_973, 1.0] {
1178 let a = time_shift_sigma(s, 12.0, 3.0);
1179 let back = time_shift_sigma(a, 3.0, 12.0);
1180 assert!((back - s).abs() < 1e-9, "{s} -> {a} -> {back}");
1181 }
1182 }
1183
1184 #[test]
1185 fn slope_matches_a_finite_difference() {
1186 let h = 1e-6;
1187 for &s in &[0.2, 0.5, 0.9] {
1188 let num = (time_shift_sigma(s + h, 12.0, 3.0) - time_shift_sigma(s - h, 12.0, 3.0))
1189 / (2.0 * h);
1190 let got = time_shift_slope(s, 12.0, 3.0);
1191 assert!((num - got).abs() < 1e-5, "{s}: {num} vs {got}");
1192 }
1193 }
1194
1195 #[test]
1196 fn patchify_round_trips() {
1197 let (c, t, h, w) = (3usize, 2usize, 4usize, 6usize);
1198 let x: Vec<f32> = (0..c * t * h * w).map(|i| i as f32).collect();
1199 let rows = patchify_video(&x, c, t, h, w);
1200 assert_eq!(rows.len(), t * (h / 2) * (w / 2) * c * 4);
1201 assert_eq!(unpatchify_video(&rows, c, t, h, w), x);
1202 }
1203
1204 #[test]
1205 fn audio_pack_round_trips() {
1206 let (c, t) = (5usize, 7usize);
1207 let x: Vec<f32> = (0..c * 2 * t).map(|i| i as f32).collect();
1208 let rows = pack_audio(&x, c, t);
1209 assert_eq!(unpack_audio(&rows, c, t), x);
1210 }
1211
1212 #[test]
1213 fn keyframes_sit_between_the_text_and_the_audio() {
1214 let (tl, lt, lh, lw, at) = (8usize, 3usize, 8usize, 12usize, 5usize);
1215 let base = Layout::t2va(tl, lt, lh, lw, at);
1216 let l = Layout::fl2va(tl, lt, lh, lw, at, &[(0, 39), (38, 39)], &[]);
1218 assert_eq!(l.cond_rows(), 2 * l.frame_rows);
1219 assert_eq!(l.seq_len, base.seq_len + 2 * l.frame_rows);
1220 let kinds: Vec<_> = l.segments.iter().map(|s| s.kind).collect();
1221 assert_eq!(
1222 kinds,
1223 vec![Kind::Text, Kind::Cond, Kind::Cond, Kind::Audio, Kind::Video]
1224 );
1225 let (a0, v0) = (l.segment(Kind::Audio), l.segment(Kind::Video));
1228 let (ba, bv) = (base.segment(Kind::Audio), base.segment(Kind::Video));
1229 assert_eq!(l.pos[a0.start][0], base.pos[ba.start][0]);
1230 assert_eq!(l.pos[v0.start][0], base.pos[bv.start][0]);
1231 let c: Vec<_> = l.segments.iter().filter(|s| s.kind == Kind::Cond).collect();
1234 assert_eq!(l.pos[c[0].start][0], tl as f64);
1235 let spans: f64 = (0..lt).map(|k| FRAME_RESCALE * FRAME_PER_TOKEN[k % 5]).sum();
1236 assert!((l.pos[c[1].start][0] - (tl as f64 + spans - FRAME_RESCALE)).abs() < 1e-12);
1237 assert_eq!(l.pos[c[0].start][1], l.pos[v0.start][1]);
1239 assert_eq!(l.pos[c[0].start][2], l.pos[v0.start][2]);
1240 }
1241
1242 #[test]
1243 fn layout_places_the_target_streams_last() {
1244 let l = Layout::t2va(8, 3, 8, 12, 5);
1245 assert_eq!(l.frame_rows, 4 * 6);
1246 assert_eq!(l.seq_len, 8 + 2 * 5 + 3 * 24);
1247 assert_eq!(l.segments[0].kind, Kind::Text);
1248 assert_eq!(l.segments[1].kind, Kind::Audio);
1249 assert_eq!(l.segments[2].kind, Kind::Video);
1250 let v = l.segment(Kind::Video);
1252 let t0 = l.pos[v.start][0];
1253 let t1 = l.pos[v.start + l.frame_rows][0];
1254 assert!((t1 - t0 - FRAME_RESCALE).abs() < 1e-12);
1255 }
1256}