1use crate::dit::cmf_f32;
18use crate::ltxvae::Vol;
19use crate::pool::Pool;
20use crate::s2v::{Rows, pool_rows, silu};
21use crate::zimage::host_gemm;
22use cortiq_core::CmfModel;
23use std::sync::Arc;
24
25pub const SPATIAL: usize = 8;
26pub const TEMPORAL: usize = 4;
27pub const SCALING_FACTOR: f32 = 0.476986;
28const CHUNK: usize = 4096;
29const GN_EPS: f64 = 1e-6;
30
31pub(crate) struct Conv {
32 pub(crate) w: Vec<f32>,
33 pub(crate) b: Vec<f32>,
34 pub(crate) cin: usize,
35 pub(crate) cout: usize,
36 pub(crate) k: [usize; 3],
37 pub(crate) stride: [usize; 3],
38}
39
40impl Conv {
41 pub(crate) fn load(m: &Arc<CmfModel>, p: &str, stride: [usize; 3]) -> Result<Conv, String> {
42 let name = format!("{p}.weight");
43 let e = m.tensor(&name).ok_or_else(|| format!("missing tensor {name}"))?;
44 if e.shape.len() != 5 {
45 return Err(format!("{name}: not a 3-D conv"));
46 }
47 let (cout, cin, k) = (e.shape[0], e.shape[1], [e.shape[2], e.shape[3], e.shape[4]]);
48 Ok(Conv { w: cmf_f32(m, &name)?, b: cmf_f32(m, &format!("{p}.bias"))?, cin, cout, k, stride })
49 }
50
51 fn forward(&self, x: &Vol, pool: Option<&Pool>) -> Vol {
52 assert_eq!(x.c, self.cin);
53 let [kt, kh, kw] = self.k;
54 let [st, sh, sw] = self.stride;
55 let (pt, ph, pw) = (kt - 1, kh / 2, kw / 2);
57 let out_f = (x.f + pt - (kt - 1) - 1) / st + 1;
58 let out_h = (x.h + 2 * ph - (kh - 1) - 1) / sh + 1;
59 let out_w = (x.w + 2 * pw - (kw - 1) - 1) / sw + 1;
60 let mut out = Vol::zeros(self.cout, out_f, out_h, out_w);
61 let npos = out_f * out_h * out_w;
62 let taps = kt * kh * kw;
63 let k = self.cin * taps;
64 let mut pat = vec![0f32; CHUNK.min(npos) * k];
65 let mut ybuf = vec![0f32; CHUNK.min(npos) * self.cout];
66 let mut p0 = 0;
67 while p0 < npos {
68 let n = CHUNK.min(npos - p0);
69 let pr = Rows(pat.as_mut_ptr());
70 pool_rows(pool, n, &|lo, hi| {
71 for i in lo..hi {
72 let p = p0 + i;
73 let (oc, rest) = (p % out_w, p / out_w);
74 let (or, of) = (rest % out_h, rest / out_h);
75 let row = unsafe { pr.at(i * k, k) };
77 for c in 0..self.cin {
78 let mut o = c * taps;
79 for dt in 0..kt {
80 let f = (of * st + dt).saturating_sub(pt).min(x.f - 1);
81 for dh in 0..kh {
82 let h = (or * sh + dh).saturating_sub(ph).min(x.h - 1);
83 for dw in 0..kw {
84 let w = (oc * sw + dw).saturating_sub(pw).min(x.w - 1);
85 row[o] = x.at(c, f, h, w);
86 o += 1;
87 }
88 }
89 }
90 }
91 }
92 });
93 host_gemm::gemm_nt(&pat[..n * k], &self.w, &mut ybuf[..n * self.cout], n, k, self.cout, pool);
94 for co in 0..self.cout {
95 let bias = self.b[co];
96 let dst = &mut out.data[co * npos + p0..co * npos + p0 + n];
97 for (i, d) in dst.iter_mut().enumerate() {
98 *d = ybuf[i * self.cout + co] + bias;
99 }
100 }
101 p0 += n;
102 }
103 out
104 }
105}
106
107pub(crate) struct GroupNorm {
108 pub(crate) w: Vec<f32>,
109 pub(crate) b: Vec<f32>,
110}
111
112impl GroupNorm {
113 pub(crate) fn load(m: &Arc<CmfModel>, p: &str) -> Result<Self, String> {
114 Ok(Self { w: cmf_f32(m, &format!("{p}.weight"))?, b: cmf_f32(m, &format!("{p}.bias"))? })
115 }
116 fn apply(&self, x: &mut Vol, pool: Option<&Pool>) {
118 let npos = x.positions();
119 let per = x.c / 32;
120 let sr = Rows(x.data.as_mut_ptr());
121 pool_rows(pool, 32, &|lo, hi| {
122 for g in lo..hi {
123 let d = unsafe { sr.at(g * per * npos, per * npos) };
125 let cnt = d.len() as f64;
126 let mean = d.iter().map(|&v| v as f64).sum::<f64>() / cnt;
127 let var = d.iter().map(|&v| (v as f64 - mean) * (v as f64 - mean)).sum::<f64>() / cnt;
128 let inv = 1.0 / (var + GN_EPS).sqrt();
129 for c in 0..per {
130 let (w, b) = (self.w[g * per + c], self.b[g * per + c]);
131 for v in &mut d[c * npos..(c + 1) * npos] {
132 *v = ((*v as f64 - mean) * inv) as f32 * w + b;
133 }
134 }
135 }
136 });
137 }
138}
139
140fn silu_all(x: &mut Vol, pool: Option<&Pool>) {
141 let n = x.data.len();
142 let sr = Rows(x.data.as_mut_ptr());
143 pool_rows(pool, n / 4096 + 1, &|lo, hi| {
144 for i in lo..hi {
145 let (a, b) = (i * 4096, ((i + 1) * 4096).min(n));
146 if a >= b {
147 continue;
148 }
149 for v in unsafe { sr.at(a, b - a) } {
151 *v = silu(*v);
152 }
153 }
154 });
155}
156
157pub(crate) struct Res {
158 pub(crate) n1: GroupNorm,
159 pub(crate) c1: Conv,
160 pub(crate) n2: GroupNorm,
161 pub(crate) c2: Conv,
162 pub(crate) short: Option<Conv>,
163}
164
165impl Res {
166 pub(crate) fn load(m: &Arc<CmfModel>, p: &str) -> Result<Res, String> {
167 let s = format!("{p}.conv_shortcut.conv.weight");
168 Ok(Res {
169 n1: GroupNorm::load(m, &format!("{p}.norm1"))?,
170 c1: Conv::load(m, &format!("{p}.conv1.conv"), [1, 1, 1])?,
171 n2: GroupNorm::load(m, &format!("{p}.norm2"))?,
172 c2: Conv::load(m, &format!("{p}.conv2.conv"), [1, 1, 1])?,
173 short: if m.tensor(&s).is_some() {
174 Some(Conv::load(m, &format!("{p}.conv_shortcut.conv"), [1, 1, 1])?)
175 } else {
176 None
177 },
178 })
179 }
180 fn forward(&self, x: &Vol, pool: Option<&Pool>) -> Vol {
181 let mut h = x.clone();
182 self.n1.apply(&mut h, pool);
183 silu_all(&mut h, pool);
184 let mut h = self.c1.forward(&h, pool);
185 self.n2.apply(&mut h, pool);
186 silu_all(&mut h, pool);
187 let mut h = self.c2.forward(&h, pool);
188 match &self.short {
189 Some(s) => {
190 let r = s.forward(x, pool);
191 for (a, &b) in h.data.iter_mut().zip(&r.data) {
192 *a += b;
193 }
194 }
195 None => {
196 for (a, &b) in h.data.iter_mut().zip(&x.data) {
197 *a += b;
198 }
199 }
200 }
201 h
202 }
203}
204
205pub(crate) struct Mid {
206 pub(crate) r0: Res,
207 pub(crate) gn: GroupNorm,
208 pub(crate) q: (Vec<f32>, Vec<f32>),
209 pub(crate) k: (Vec<f32>, Vec<f32>),
210 pub(crate) v: (Vec<f32>, Vec<f32>),
211 pub(crate) o: (Vec<f32>, Vec<f32>),
212 pub(crate) r1: Res,
213}
214
215impl Mid {
216 pub(crate) fn load(m: &Arc<CmfModel>, p: &str) -> Result<Mid, String> {
217 let a = format!("{p}.attentions.0");
218 let lin = |n: &str| -> Result<(Vec<f32>, Vec<f32>), String> {
219 Ok((cmf_f32(m, &format!("{a}.{n}.weight"))?, cmf_f32(m, &format!("{a}.{n}.bias"))?))
220 };
221 Ok(Mid {
222 r0: Res::load(m, &format!("{p}.resnets.0"))?,
223 gn: GroupNorm::load(m, &format!("{a}.group_norm"))?,
224 q: lin("to_q")?,
225 k: lin("to_k")?,
226 v: lin("to_v")?,
227 o: lin("to_out.0")?,
228 r1: Res::load(m, &format!("{p}.resnets.1"))?,
229 })
230 }
231
232 fn attention(&self, x: &Vol, pool: Option<&Pool>) -> Vol {
233 let c = x.c;
234 let n = x.positions();
235 let per_frame = x.h * x.w;
236 let mut y = x.clone();
237 self.gn.apply(&mut y, pool);
238 let mut rows = vec![0f32; n * c];
240 for ch in 0..c {
241 for p in 0..n {
242 rows[p * c + ch] = y.data[ch * n + p];
243 }
244 }
245 let lin = |l: &(Vec<f32>, Vec<f32>), s: &[f32], m: usize| -> Vec<f32> {
246 let mut out = vec![0f32; m * c];
247 host_gemm::gemm_nt(s, &l.0, &mut out, m, c, c, pool);
248 for r in out.chunks_exact_mut(c) {
249 for (v, &b) in r.iter_mut().zip(&l.1) {
250 *v += b;
251 }
252 }
253 out
254 };
255 let q = lin(&self.q, &rows, n);
256 let k = lin(&self.k, &rows, n);
257 let v = lin(&self.v, &rows, n);
258 drop(rows);
259 let scale = 1.0 / (c as f32).sqrt();
260 let mut att = vec![0f32; n * c];
263 for f in 0..x.f {
264 let live = (f + 1) * per_frame;
265 let mut s = vec![0f32; per_frame * live];
266 let qh: Vec<f32> = q[f * per_frame * c..(f + 1) * per_frame * c].iter().map(|&a| a * scale).collect();
267 host_gemm::gemm_nt(&qh, &k[..live * c], &mut s, per_frame, c, live, pool);
268 let sr = Rows(s.as_mut_ptr());
269 pool_rows(pool, per_frame, &|lo, hi| {
270 for r in lo..hi {
271 let row = unsafe { sr.at(r * live, live) };
273 let mx = row.iter().cloned().fold(f32::MIN, f32::max);
274 let mut den = 0f64;
275 for v in row.iter_mut() {
276 *v = (*v - mx).exp();
277 den += *v as f64;
278 }
279 let inv = (1.0 / den) as f32;
280 for v in row.iter_mut() {
281 *v *= inv;
282 }
283 }
284 });
285 let mut vt = vec![0f32; c * live];
286 for d in 0..c {
287 for r in 0..live {
288 vt[d * live + r] = v[r * c + d];
289 }
290 }
291 host_gemm::gemm_nt(&s, &vt, &mut att[f * per_frame * c..(f + 1) * per_frame * c], per_frame, live, c, pool);
292 }
293 let o = lin(&self.o, &att, n);
294 let mut out = x.clone();
296 for ch in 0..c {
297 for p in 0..n {
298 out.data[ch * n + p] += o[p * c + ch];
299 }
300 }
301 out
302 }
303
304 fn forward(&self, x: &Vol, pool: Option<&Pool>) -> Vol {
305 let h = self.r0.forward(x, pool);
306 let h = self.attention(&h, pool);
307 self.r1.forward(&h, pool)
308 }
309}
310
311pub(crate) struct Up {
312 pub(crate) res: Vec<Res>,
313 pub(crate) conv: Option<(Conv, [usize; 3])>,
314}
315
316fn nearest_up(x: &Vol, ft: usize, fs: usize) -> Vol {
318 let f_out = if ft == 2 { 1 + (x.f - 1) * 2 } else { x.f };
319 let (h_out, w_out) = (x.h * fs, x.w * fs);
320 let mut out = Vol::zeros(x.c, f_out, h_out, w_out);
321 let (ni, no) = (x.positions(), f_out * h_out * w_out);
322 let src_f = |of: usize| if ft == 2 && of > 0 { 1 + (of - 1) / 2 } else { of };
323 for c in 0..x.c {
324 for of in 0..f_out {
325 let sf = src_f(of);
326 for oh in 0..h_out {
327 let sh = oh / fs;
328 for ow in 0..w_out {
329 let sw = ow / fs;
330 out.data[c * no + ((of * h_out + oh) * w_out + ow)] =
331 x.data[c * ni + ((sf * x.h + sh) * x.w + sw)];
332 }
333 }
334 }
335 }
336 out
337}
338
339pub struct S2vVae {
340 pub(crate) post_quant: Conv,
341 pub(crate) conv_in: Conv,
342 pub(crate) mid: Mid,
343 pub(crate) ups: Vec<Up>,
344 pub(crate) norm_out: GroupNorm,
345 pub(crate) conv_out: Conv,
346 pub(crate) pool: Option<Arc<Pool>>,
347}
348
349pub struct S2vVaeEncoder {
350 pub(crate) quant: Conv,
351 pub(crate) conv_in: Conv,
352 pub(crate) downs: Vec<(Vec<Res>, Option<Conv>)>,
353 pub(crate) mid: Mid,
354 pub(crate) norm_out: GroupNorm,
355 pub(crate) conv_out: Conv,
356 pub(crate) pool: Option<Arc<Pool>>,
357}
358
359impl S2vVae {
360 pub fn from_cmf(m: &Arc<CmfModel>) -> Result<S2vVae, String> {
361 let mut ups = Vec::new();
362 for (i, factor) in [[1, 2, 2], [2, 2, 2], [2, 2, 2], [1, 1, 1]].into_iter().enumerate() {
363 let p = format!("vae.decoder.up_blocks.{i}");
364 let mut res = Vec::new();
365 for r in 0..3 {
366 res.push(Res::load(m, &format!("{p}.resnets.{r}"))?);
367 }
368 let conv = if i < 3 {
369 Some((Conv::load(m, &format!("{p}.upsamplers.0.conv.conv"), [1, 1, 1])?, factor))
370 } else {
371 None
372 };
373 ups.push(Up { res, conv });
374 }
375 Ok(S2vVae {
376 post_quant: Conv::load(m, "vae.post_quant_conv", [1, 1, 1])?,
377 conv_in: Conv::load(m, "vae.decoder.conv_in.conv", [1, 1, 1])?,
378 mid: Mid::load(m, "vae.decoder.mid_block")?,
379 ups,
380 norm_out: GroupNorm::load(m, "vae.decoder.conv_norm_out")?,
381 conv_out: Conv::load(m, "vae.decoder.conv_out.conv", [1, 1, 1])?,
382 pool: Pool::from_env(),
383 })
384 }
385
386 pub(crate) fn post_quant_host(&self, z: &Vol) -> Vol {
388 self.post_quant.forward(z, self.pool.as_deref())
389 }
390
391 fn decode_tile(&self, z: &Vol) -> Vol {
393 let pool = self.pool.as_deref();
394 let h = self.post_quant.forward(z, pool);
395 let h = self.conv_in.forward(&h, pool);
396 let mut h = self.mid.forward(&h, pool);
397 for up in &self.ups {
398 for r in &up.res {
399 h = r.forward(&h, pool);
400 }
401 if let Some((conv, f)) = &up.conv {
402 let u = nearest_up(&h, f[0], f[1]);
403 h = conv.forward(&u, pool);
404 }
405 }
406 self.norm_out.apply(&mut h, pool);
407 silu_all(&mut h, pool);
408 self.conv_out.forward(&h, pool)
409 }
410
411 pub fn decode_with(&self, z: &Vol, tile: &mut dyn FnMut(&Vol) -> Vol) -> Vol {
414 tiled(z, tile)
415 }
416
417 pub fn decode(&self, z: &Vol) -> Vol {
418 tiled(z, &mut |t| self.decode_tile(t))
419 }
420}
421
422pub(crate) fn tiled(z: &Vol, tile: &mut dyn FnMut(&Vol) -> Vol) -> Vol {
424 const MIN_LAT: usize = 4;
425 const STRIDE_LAT: usize = 3;
426 const STRIDE_PX: usize = 12;
427 const BLEND: usize = 4;
428 let frames_out = (z.f - 1) * TEMPORAL + 1;
429 let mut row: Vec<Vol> = Vec::new();
430 let mut i = 0;
431 while i < z.f {
432 let take = (MIN_LAT + 1).min(z.f - i);
433 let mut t = Vol::zeros(z.c, take, z.h, z.w);
434 let per = z.h * z.w;
435 for c in 0..z.c {
436 let src = &z.data[c * z.positions() + i * per..c * z.positions() + (i + take) * per];
437 t.data[c * take * per..(c + 1) * take * per].copy_from_slice(src);
438 }
439 let mut d = tile(&t);
440 if i > 0 {
441 d = drop_first_frame(&d);
442 }
443 row.push(d);
444 i += STRIDE_LAT;
445 }
446 let first = &row[0];
447 let (h, w) = (first.h, first.w);
448 let per = h * w;
449 let mut out = Vol::zeros(first.c, frames_out, h, w);
450 let mut at = 0usize;
451 for ti in 0..row.len() {
452 let keep = if ti == 0 { STRIDE_PX + 1 } else { STRIDE_PX };
453 let tl = &row[ti];
454 let take = keep.min(tl.f).min(frames_out - at);
455 for fr in 0..take {
456 for c in 0..tl.c {
457 let cb = c * tl.f * per + fr * per;
458 let dst = &mut out.data[c * frames_out * per + (at + fr) * per..][..per];
459 if ti > 0 && fr < BLEND.min(row[ti - 1].f).min(tl.f) {
460 let prev = &row[ti - 1];
465 let ext = BLEND.min(prev.f).min(tl.f);
466 let pa = c * prev.f * per + (prev.f - ext + fr) * per;
467 let wgt = fr as f32 / ext as f32;
468 for j in 0..per {
469 dst[j] = prev.data[pa + j] * (1.0 - wgt) + tl.data[cb + j] * wgt;
470 }
471 } else {
472 dst.copy_from_slice(&tl.data[cb..cb + per]);
473 }
474 }
475 }
476 at += take;
477 }
478 out
479}
480
481fn drop_first_frame(v: &Vol) -> Vol {
482 let per = v.h * v.w;
483 let mut o = Vol::zeros(v.c, v.f - 1, v.h, v.w);
484 for c in 0..v.c {
485 let s = c * v.f * per + per;
486 o.data[c * (v.f - 1) * per..(c + 1) * (v.f - 1) * per].copy_from_slice(&v.data[s..s + (v.f - 1) * per]);
487 }
488 o
489}
490
491impl S2vVaeEncoder {
492 pub fn from_cmf(m: &Arc<CmfModel>) -> Result<Self, String> {
493 let mut downs = Vec::new();
494 for (i, st) in [[1, 2, 2], [2, 2, 2], [2, 2, 2], [1, 1, 1]].into_iter().enumerate() {
495 let p = format!("vae.encoder.down_blocks.{i}");
496 let mut res = Vec::new();
497 for r in 0..2 {
498 res.push(Res::load(m, &format!("{p}.resnets.{r}"))?);
499 }
500 let d = if i < 3 {
501 Some(Conv::load(m, &format!("{p}.downsamplers.0.conv.conv"), st)?)
502 } else {
503 None
504 };
505 downs.push((res, d));
506 }
507 Ok(Self {
508 quant: Conv::load(m, "vae.quant_conv", [1, 1, 1])?,
509 conv_in: Conv::load(m, "vae.encoder.conv_in.conv", [1, 1, 1])?,
510 downs,
511 mid: Mid::load(m, "vae.encoder.mid_block")?,
512 norm_out: GroupNorm::load(m, "vae.encoder.conv_norm_out")?,
513 conv_out: Conv::load(m, "vae.encoder.conv_out.conv", [1, 1, 1])?,
514 pool: Pool::from_env(),
515 })
516 }
517
518 pub fn encode(&self, img: &Vol) -> (Vol, Vol) {
520 let pool = self.pool.as_deref();
521 let mut h = self.conv_in.forward(img, pool);
522 for (res, d) in &self.downs {
523 for r in res {
524 h = r.forward(&h, pool);
525 }
526 if let Some(d) = d {
527 h = d.forward(&h, pool);
528 }
529 }
530 let mut h = self.mid.forward(&h, pool);
531 self.norm_out.apply(&mut h, pool);
532 silu_all(&mut h, pool);
533 let h = self.conv_out.forward(&h, pool);
534 self.finish_host(&h)
535 }
536
537 pub(crate) fn finish_host(&self, h: &Vol) -> (Vol, Vol) {
539 let h = self.quant.forward(h, self.pool.as_deref());
540 let n = h.positions();
541 let mut mean = Vol::zeros(16, h.f, h.h, h.w);
542 let mut lv = Vol::zeros(16, h.f, h.h, h.w);
543 mean.data.copy_from_slice(&h.data[..16 * n]);
544 lv.data.copy_from_slice(&h.data[16 * n..32 * n]);
545 for v in &mut lv.data {
546 *v = v.clamp(-30.0, 20.0);
547 }
548 (mean, lv)
549 }
550}