"""Phonon (gradium/phonon-31m-en) building blocks for recipes/phonon-31m-en.py: weights, eager reference, export programs. The eager reference follows the official runtime (the `ptts` crate, src/{flow_lm,tts_model, transformer,mlp,conditioners,mimi,seanet,conv}.rs) operation by operation: voice speaker_wavs [1,512,125] -> transpose -> speaker_proj (512x512, no bias) -> [1,125,512] prime transformer(voice) then transformer(text_embed(tokens)), both written to the KV cache step x = input_linear(prev latent or bos_emb) + cond (cond = num_speakers("1") LUT + duration_delta(0.0) sinusoid, each through its output_proj) h = out_norm(transformer(x))[:, -1]; eos_logit = out_eos(h) latent = noise + flow_net(h, s=0, t=1, noise) (LSD, 1 step; noise ~ N(0, temp)) decode (latent * emb_std + emb_mean) -> 1x1 conv 32->512 -> depthwise convtr x16 -> 2-layer transformer (window 250, layer scale) -> SEANet decoder (ratios 6,5,4) -> 1920 samples Export programs (WebGPU): FlowLMProgram forward(embeds[1,S,512] S<=128, input_pos[1] i64, noise[1,32]) -> latent[1,32], eos_logit[1,1], next_embeds[1,1,512] (KV cache in the program) MimiStepProgram forward(latent[1,1,32], *state) -> pcm[1,1,1920], *new_state (state explicit) Rewrites for the public WebGPU runtime (same function, checked against the eager reference): * RoPE: q/k rows of in_proj permuted from interleaved pairs to halves, so the runtime's fused HF RoPE (et_vk.apply_rotary_emb_hf) applies; q.k is unchanged. * flow-LM LayerNorm (dynamic S): mean as a 1/512 linear + the rms_norm pattern, because native_layer_norm has no resize hook and mean.dim's resize drops a keepdim'ed dim. * ELU = relu(x) + exp(min(x, 0)) - 1 and SiLU = x * sigmoid(x) (no aten.elu / aten.silu). * ConvTranspose1d as a 4-D (H = 1) conv_transpose2d (3-D convolution has no transposed path). * Mimi streaming state as explicit tensors: mutable buffers other than the llama KV cache are not kept live by the delegate. """ import math import torch import torch.nn.functional as F from torch import nn D = 512 H = 8 HD = 64 LDIM = 32 FLOW_LAYERS = 6 MIMI_LAYERS = 2 MIMI_CONTEXT = 250 FRAME = 1920 # samples per latent frame at 24 kHz UPS = 16 # mimi transformer tokens per latent frame NEG = -1.0e30 # --------------------------------------------------------------------------------------------- # Weights: a module whose state_dict keys are exactly the checkpoint's keys. # --------------------------------------------------------------------------------------------- def _p(*shape): return nn.Parameter(torch.zeros(*shape), requires_grad=False) class _Lin(nn.Module): def __init__(self, i, o, bias): super().__init__() self.weight = _p(o, i) if bias: self.bias = _p(o) class _LN(nn.Module): def __init__(self, n): super().__init__() self.weight = _p(n) self.bias = _p(n) class _Attn(nn.Module): def __init__(self): super().__init__() self.in_proj = _Lin(D, 3 * D, False) self.out_proj = _Lin(D, D, False) class _Scale(nn.Module): def __init__(self): super().__init__() self.scale = _p(D) class _Layer(nn.Module): def __init__(self, layer_scale): super().__init__() self.self_attn = _Attn() self.norm1 = _LN(D) self.norm2 = _LN(D) self.linear1 = _Lin(D, 2048, False) self.linear2 = _Lin(2048, D, False) if layer_scale: self.layer_scale_1 = _Scale() self.layer_scale_2 = _Scale() class _Transformer(nn.Module): def __init__(self, n, layer_scale): super().__init__() self.layers = nn.ModuleList(_Layer(layer_scale) for _ in range(n)) class _Seq(nn.Module): """nn.Sequential-style numbered children with only the indices that hold weights.""" def __init__(self, items): super().__init__() for k, m in items.items(): self.add_module(str(k), m) def __getitem__(self, i): return getattr(self, str(i)) class _TimeEmbed(nn.Module): def __init__(self): super().__init__() self.freqs = _p(128) self.mlp = _Seq({0: _Lin(256, D, True), 2: _Lin(D, D, True), 3: _Alpha()}) class _Alpha(nn.Module): def __init__(self): super().__init__() self.alpha = _p(D) class _ResBlock(nn.Module): def __init__(self): super().__init__() self.in_ln = _LN(D) self.mlp = _Seq({0: _Lin(D, D, True), 2: _Lin(D, D, True)}) self.adaLN_modulation = _Seq({1: _Lin(D, 3 * D, True)}) class _Final(nn.Module): def __init__(self): super().__init__() self.linear = _Lin(D, LDIM, True) self.adaLN_modulation = _Seq({1: _Lin(D, 2 * D, True)}) class _FlowNet(nn.Module): def __init__(self, depth=6): super().__init__() self.time_embed = nn.ModuleList([_TimeEmbed(), _TimeEmbed()]) self.cond_embed = _Lin(D, D, True) self.input_proj = _Lin(LDIM, D, True) self.res_blocks = nn.ModuleList(_ResBlock() for _ in range(depth)) self.final_layer = _Final() class _Embed(nn.Module): def __init__(self, n, d): super().__init__() self.weight = _p(n, d) class _Cond(nn.Module): def __init__(self, embed=None, proj_in=None): super().__init__() self.learnt_padding = _p(1, 1, D) if embed is not None: self.embed = _Embed(*embed) if proj_in is not None: self.output_proj = _Lin(proj_in, D, False) class _Conditioners(nn.Module): def __init__(self): super().__init__() self.duration_delta = _Cond(proj_in=128) self.num_speakers = _Cond(embed=(32, 16), proj_in=16) self.speaker_wavs = _Cond(proj_in=512) self.transcript_in_segment = _Cond(embed=(4001, D)) class _CondProvider(nn.Module): def __init__(self): super().__init__() self.conditioners = _Conditioners() class _WST(nn.Module): """flow.w_s_t: a training-time loss-weighting net; the runtime drops it (loader.rs remap_key).""" def __init__(self): super().__init__() self.w_s_t = _Seq( { 0: _Lin(2, 32, True), 3: _Lin(32, 32, True), 6: _Lin(32, 32, True), 9: _Lin(32, 1, True), } ) class FlowLMWeights(nn.Module): def __init__(self): super().__init__() self.bos_emb = _p(LDIM) self.emb_mean = _p(LDIM) self.emb_std = _p(LDIM) self.condition_provider = _CondProvider() self.flow = _WST() self.flow_net = _FlowNet() self.input_linear = _Lin(LDIM, D, False) self.out_eos = _Lin(D, 1, True) self.out_norm = _LN(D) self.transformer = _Transformer(FLOW_LAYERS, False) class _Conv(nn.Module): def __init__(self, i, o, k): super().__init__() self.conv = _ConvW(i, o, k, bias=True) class _ConvW(nn.Module): def __init__(self, i, o, k, bias): super().__init__() self.weight = _p(o, i, k) if bias: self.bias = _p(o) class _ConvTr(nn.Module): def __init__(self, i, o, k): super().__init__() self.convtr = _ConvTrW(i, o, k, True) class _ConvTrW(nn.Module): def __init__(self, i, o_per_group, k, bias): super().__init__() self.weight = _p(i, o_per_group, k) if bias: self.bias = _p(o_per_group) class _Res(nn.Module): def __init__(self, dim): super().__init__() self.block = _Seq({1: _Conv(dim, dim // 2, 3), 3: _Conv(dim // 2, dim, 1)}) class _SEANetDecoder(nn.Module): def __init__(self): super().__init__() self.model = _Seq( { 0: _Conv(512, 512, 7), 2: _ConvTr(512, 256, 12), 3: _Res(256), 5: _ConvTr(256, 128, 10), 6: _Res(128), 8: _ConvTr(128, 64, 8), 9: _Res(64), 11: _Conv(64, 1, 3), } ) class _ProjT(nn.Module): def __init__(self): super().__init__() self.transformer = _Transformer(MIMI_LAYERS, True) class _Quantizer(nn.Module): def __init__(self): super().__init__() self.output_proj = _ConvW(LDIM, D, 1, bias=False) class _Upsample(nn.Module): def __init__(self): super().__init__() self.convtr = _Seq({}) self.convtr.convtr = _ConvTrW(D, 1, 2 * UPS, False) class MimiWeights(nn.Module): def __init__(self): super().__init__() self.decoder = _SEANetDecoder() self.decoder_transformer = _ProjT() self.quantizer = _Quantizer() self.upsample = _Upsample() class PhononWeights(nn.Module): def __init__(self): super().__init__() self.flow_lm = FlowLMWeights() self.mimi = MimiWeights() def load_weights(safetensors_path): """Every checkpoint tensor, as float32 (bf16 -> f32 is exact), loaded with strict=True.""" from safetensors.torch import load_file sd = {k: v.float() for k, v in load_file(str(safetensors_path)).items()} w = PhononWeights() w.load_state_dict(sd, strict=True) return w.eval() def load_voice(path): """speaker_wavs [1, 512, T] (stored speaker-Mimi latents) from a voice .safetensors file.""" from safetensors.torch import load_file t = load_file(str(path)) assert list(t) == ["speaker_wavs"], list(t) return t["speaker_wavs"].float() # --------------------------------------------------------------------------------------------- # Shared math # --------------------------------------------------------------------------------------------- def lin(m, x): """nn.Linear on a 2-D view (the public WebGPU runtime's fp32 aten.linear takes 2-D input).""" shp = x.shape y = F.linear(x.reshape(-1, shp[-1]), m.weight, getattr(m, "bias", None)) return y.reshape(*shp[:-1], y.shape[-1]) def lin3(m, x): """nn.Linear on the tensor as it is (any rank); the Vulkan lowering adds its own 2-D views.""" return F.linear(x, m.weight, getattr(m, "bias", None)) def layer_norm_dyn(m, x, eps, mean_w): """LayerNorm for the dynamic-length flow-LM program, from ops that follow a resized sequence length on the public WebGPU runtime (its native_layer_norm has no resize hook, and its mean.dim resize drops a keepdim'ed dim): the mean is a linear with weight 1/512 (exact: a power of two), the rest matches the Vulkan rms_norm pattern (fused into et_vk.rms_norm).""" xc = x - F.linear(x, mean_w) return xc * torch.rsqrt((xc * xc).mean(-1, keepdim=True) + eps) * m.weight + m.bias def layer_norm(m, x, eps): return F.layer_norm(x, (x.shape[-1],), m.weight, m.bias, eps) def silu(x): return x * torch.sigmoid(x) def elu(x): """ELU(alpha=1) from ops the public WebGPU runtime registers (it has no aten.elu).""" return torch.relu(x) + torch.exp(torch.clamp(x, max=0.0)) - 1.0 def gelu(x): return F.gelu(x) # erf def inv_freq(): """RoPE frequencies as the runtime computes them (rope.rs): 1 / max_period^(i / half) in f32.""" half = HD // 2 i = torch.arange(half, dtype=torch.float32) return 1.0 / torch.pow(torch.tensor(10000.0, dtype=torch.float32), i / half) def rope_interleaved(x, pos): """rope_i on [B, T, H, HD] with absolute positions pos [T] (the runtime's convention: pairs (2i, 2i+1) rotate by pos * inv_freq[i]).""" ang = pos.to(torch.float32)[:, None] * inv_freq()[None, :] # [T, half] c, s = torch.cos(ang)[None, :, None, :], torch.sin(ang)[None, :, None, :] xr, xi = x[..., 0::2], x[..., 1::2] return torch.stack([xr * c - xi * s, xr * s + xi * c], dim=-1).flatten(-2) def time_embed_rms(te, t): """TimestepEmbedder with TimeRmsNorm::Rms (phonon config time_rms_norm: rms).""" args = t * te.freqs emb = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) x = lin(te.mlp[2], silu(lin(te.mlp[0], emb))) ms = (x * x).mean(-1, keepdim=True) return x * torch.rsqrt(ms + 1e-5) * te.mlp[3].alpha def flow_net(fn, c, x, t_comb): """SimpleMLPAdaLN: c [N, 512] backbone output, x [N, 32] noise, t_comb [N or 1, 512].""" x = lin(fn.input_proj, x) y = t_comb + lin(fn.cond_embed, c) sy = silu(y) for b in fn.res_blocks: ada = lin(b.adaLN_modulation[1], sy) shift, scale, gate = ada[..., :D], ada[..., D : 2 * D], ada[..., 2 * D :] h = layer_norm(b.in_ln, x, 1e-6) * (scale + 1.0) + shift h = lin(b.mlp[2], silu(lin(b.mlp[0], h))) x = x + gate * h fl = fn.final_layer ada = lin(fl.adaLN_modulation[1], sy) shift, scale = ada[..., :D], ada[..., D:] x = F.layer_norm(x, (D,), None, None, 1e-6) * (scale + 1.0) + shift return lin(fl.linear, x) def lsd_time(fn): """(time_embed[0](s=0) + time_embed[1](t=1)) / 2, the flow head's constant time condition for lsd_decode_steps = 1.""" s = torch.zeros(1, 1) t = torch.ones(1, 1) return ( time_embed_rms(fn.time_embed[0], s) + time_embed_rms(fn.time_embed[1], t) ) / 2 def sin_embedding(position, dim, max_period): half = dim // 2 i = torch.arange(half, dtype=torch.float32) ph = position / torch.pow( torch.tensor(max_period, dtype=torch.float32), i / (half - 1) ) return torch.cat([torch.cos(ph), torch.sin(ph)]) def summed_conditions(flw, num_speakers="1", duration_delta=0.0): """conditioners.rs Conditioners::sum with the runtime's defaults, [1, 1, 512].""" c = flw.condition_provider.conditioners idx = ["0", "1", "2"].index(num_speakers) ns = F.linear( c.num_speakers.embed.weight[idx : idx + 1], c.num_speakers.output_proj.weight ) dd = F.linear( sin_embedding(1000.0 * duration_delta, 128, 10000.0)[None], c.duration_delta.output_proj.weight, ) return (ns + dd).view(1, 1, D) def voice_embedding(flw, speaker_wavs): """[1, 512, T] speaker latents -> [1, T, 512] voice prompt (loader.rs project_latents).""" w = flw.condition_provider.conditioners.speaker_wavs.output_proj.weight return F.linear(speaker_wavs.transpose(1, 2), w) def text_embedding(flw, tokens): emb = flw.condition_provider.conditioners.transcript_in_segment.embed.weight return emb[torch.as_tensor(tokens, dtype=torch.long)][None] # --------------------------------------------------------------------------------------------- # Eager reference (Python-side state, mirrors the runtime) # --------------------------------------------------------------------------------------------- class FlowLMRef: def __init__(self, w: PhononWeights, num_speakers="1", duration_delta=0.0): self.w = w.flow_lm self.cond = summed_conditions(self.w, num_speakers, duration_delta) self.t_comb = lsd_time(self.w.flow_net) self.reset() def reset(self): self.k = [None] * FLOW_LAYERS self.v = [None] * FLOW_LAYERS self.pos = 0 @torch.no_grad() def backbone(self, x): B, T, _ = x.shape pos = torch.arange(self.pos, self.pos + T) for i, L in enumerate(self.w.transformer.layers): h = layer_norm(L.norm1, x, 1e-5) qkv = lin(L.self_attn.in_proj, h).view(B, T, 3, H, HD) q, k, v = qkv[:, :, 0], qkv[:, :, 1], qkv[:, :, 2] q, k = rope_interleaved(q, pos), rope_interleaved(k, pos) self.k[i] = k if self.k[i] is None else torch.cat([self.k[i], k], 1) self.v[i] = v if self.v[i] is None else torch.cat([self.v[i], v], 1) kk, vv = self.k[i].transpose(1, 2), self.v[i].transpose(1, 2) qq = q.transpose(1, 2) Tk = kk.shape[2] qpos = torch.arange(Tk - T, Tk)[:, None] mask = torch.where(torch.arange(Tk)[None, :] <= qpos, 0.0, float("-inf")) att = torch.softmax(qq @ kk.transpose(-1, -2) / math.sqrt(HD) + mask, -1) o = (att @ vv).transpose(1, 2).reshape(B, T, D) x = x + lin(L.self_attn.out_proj, o) h = layer_norm(L.norm2, x, 1e-5) x = x + lin(L.linear2, gelu(lin(L.linear1, h))) self.pos += T return x def prompt(self, embeds): self.backbone(embeds) @torch.no_grad() def step(self, prev_latent, noise): """prev_latent None (BOS) or [1, 1, 32]; noise [1, 32]. -> latent [1,1,32], eos_logit.""" seq = self.w.bos_emb.view(1, 1, LDIM) if prev_latent is None else prev_latent x = lin(self.w.input_linear, seq) + self.cond h = layer_norm(self.w.out_norm, self.backbone(x), 1e-5)[:, -1] eos = lin(self.w.out_eos, h) latent = noise + flow_net(self.w.flow_net, h, noise, self.t_comb) return latent.view(1, 1, LDIM), eos def _conv1d_stream(m, x, prev, dilation=1): xx = torch.cat([prev, x], 2) if prev is not None else x y = F.conv1d(xx, m.conv.weight, m.conv.bias, dilation=dilation) tp = prev.shape[2] if prev is not None else 0 return y, (xx[:, :, xx.shape[2] - tp :] if tp else None) def _convtr_stream(w, bias, x, partial, stride, groups=1): y = F.conv_transpose1d(x, w, bias, stride=stride, groups=groups) pt = partial.shape[2] y = torch.cat([y[:, :, :pt] + partial, y[:, :, pt:]], 2) new_partial = y[:, :, y.shape[2] - pt :] if bias is not None: new_partial = new_partial - bias.view(1, -1, 1) return y[:, :, : y.shape[2] - pt], new_partial class MimiRef: """Streaming Mimi decoder (MimiDecoder::decode_from_latent_step), one or more frames per call.""" def __init__(self, w: PhononWeights): self.w = w.mimi self.emb_std = w.flow_lm.emb_std self.emb_mean = w.flow_lm.emb_mean self.reset() def reset(self): z = lambda *s: torch.zeros(1, *s) # noqa: E731 self.up = z(D, UPS) self.kv = [[None, None] for _ in range(MIMI_LAYERS)] self.offset = 0 self.prev = { "init": z(512, 6), "r1": z(256, 2), "r2": z(128, 2), "r3": z(64, 2), "final": z(64, 2), } self.partial = {"u1": z(256, 6), "u2": z(128, 5), "u3": z(64, 4)} @torch.no_grad() def transformer(self, x): # x [1, T, 512] T = x.shape[1] pos = torch.arange(self.offset, self.offset + T) for i, L in enumerate(self.w.decoder_transformer.transformer.layers): h = layer_norm(L.norm1, x, 1e-5) qkv = lin(L.self_attn.in_proj, h).view(1, T, 3, H, HD) q, k, v = qkv[:, :, 0], qkv[:, :, 1], qkv[:, :, 2] q, k = rope_interleaved(q, pos), rope_interleaved(k, pos) q, k, v = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2) pk, pv = self.kv[i] kk = k if pk is None else torch.cat([pk, k], 2) vv = v if pv is None else torch.cat([pv, v], 2) kvlen = kk.shape[2] - T qi = torch.arange(T)[:, None] + kvlen kj = torch.arange(kk.shape[2])[None, :] ok = (kj <= qi) & (kj >= qi - MIMI_CONTEXT) mask = torch.where(ok, 0.0, float("-inf")) att = torch.softmax(q @ kk.transpose(-1, -2) / math.sqrt(HD) + mask, -1) o = (att @ vv).transpose(1, 2).reshape(1, T, D) self.kv[i] = [kk[:, :, -MIMI_CONTEXT:], vv[:, :, -MIMI_CONTEXT:]] x = x + L.layer_scale_1.scale * lin(L.self_attn.out_proj, o) h = layer_norm(L.norm2, x, 1e-5) x = x + L.layer_scale_2.scale * lin(L.linear2, gelu(lin(L.linear1, h))) self.offset += T return x def _res(self, blk, x, key): v, self.prev[key] = _conv1d_stream(blk.block[1], elu(x), self.prev[key]) v, _ = _conv1d_stream(blk.block[3], elu(v), None) return x + v @torch.no_grad() def decode(self, latent): """latent [1, T, 32] (flow-LM space) -> pcm [1, 1, T * 1920].""" x = (latent * self.emb_std + self.emb_mean).transpose(1, 2) x = F.conv1d(x, self.w.quantizer.output_proj.weight) x, self.up = _convtr_stream( self.w.upsample.convtr.convtr.weight, None, x, self.up, UPS, groups=D ) x = self.transformer(x.transpose(1, 2)).transpose(1, 2) m = self.w.decoder.model x, self.prev["init"] = _conv1d_stream(m[0], x, self.prev["init"]) for up, res, s, ku, kr in [ (m[2], m[3], 6, "u1", "r1"), (m[5], m[6], 5, "u2", "r2"), (m[8], m[9], 4, "u3", "r3"), ]: x, self.partial[ku] = _convtr_stream( up.convtr.weight, up.convtr.bias, elu(x), self.partial[ku], s ) x = self._res(res, x, kr) x, self.prev["final"] = _conv1d_stream(m[11], elu(x), self.prev["final"]) return x # --------------------------------------------------------------------------------------------- # Generation policy (plan.rs, tts_model.rs) # --------------------------------------------------------------------------------------------- def prepare_text_prompt(text): text = " ".join(text.strip().replace("\n", " ").replace("\r", " ").split()) if not text: return text, 3 frames_after_eos = 3 if len(text.split()) <= 4 else 1 text = text[0].upper() + text[1:] if text[-1].isalnum(): text += "." return text, frames_after_eos def frame_budget(num_tokens, frame_rate=12.5): return math.ceil((num_tokens / 3.0 + 2.0) * frame_rate) class EosPolicy: def __init__(self, frames_after_eos): self.n, self.countdown = frames_after_eos, None def should_stop(self, is_eos): if is_eos and self.countdown is None: self.countdown = self.n if self.countdown is None: return False if self.countdown == 0: return True self.countdown -= 1 return False def generate(step_fn, tokens, frames_after_eos, noise_fn, eos_threshold=4.0): """The runtime's loop (synth.rs run_inline): BOS first, the EOS frame and its tail are kept. step_fn(prev_latent_or_None, noise) -> (latent [1,1,32], eos_logit). Returns latents, eos logits.""" eos = EosPolicy(frames_after_eos) prev, lats, logits = None, [], [] for i in range(frame_budget(len(tokens))): lat, e = step_fn(prev, noise_fn(i)) lats.append(lat) logits.append(float(e.reshape(-1)[0])) if eos.should_stop(logits[-1] > eos_threshold): break prev = lat return lats, logits def tokenizer(tokenizer_json): from tokenizers import Tokenizer return Tokenizer.from_file(str(tokenizer_json)) def tokenize(tok, text): prepared, frames_after_eos = prepare_text_prompt(text) return ( tok.encode(prepared, add_special_tokens=False).ids, frames_after_eos, prepared, ) # --------------------------------------------------------------------------------------------- # Export programs # --------------------------------------------------------------------------------------------- MAX_CTX = 512 # flow-LM KV cache positions: voice prompt + text tokens + generated frames PREFILL_MAX = 128 # most positions one flow-LM call may write (the 125-frame voice prompt fits) def _half_split_perm(): """Within each head, reorder q/k channels from interleaved pairs (2i, 2i+1) to halves (i, i + 32): rope_i on the old layout equals rotate_half RoPE on the new one, and q.k is unchanged because q and k are permuted alike.""" per = torch.cat([torch.arange(0, HD, 2), torch.arange(1, HD, 2)]) return torch.cat([h * HD + per for h in range(H)]) def _permuted_in_proj(w): """in_proj weight [3*512, 512] with the q and k row blocks permuted, v untouched.""" perm = _half_split_perm() return torch.cat([w[:D][perm], w[D : 2 * D][perm], w[2 * D :]], 0).contiguous() def rope_tables(n): """cos/sin [n, 64] in the half-split layout, angle = f32(pos) * f32(inv_freq) as rope.rs.""" ang = torch.arange(n, dtype=torch.float32)[:, None] * inv_freq()[None, :] c, s = torch.cos(ang), torch.sin(ang) return torch.cat([c, c], -1).contiguous(), torch.cat([s, s], -1).contiguous() def _rope_hf(q, k, cos, sin): from executorch.backends.vulkan.patterns.rope_hf import HfRotaryEmbeddingPattern return HfRotaryEmbeddingPattern()(q, k, cos, sin) class _LinP(nn.Module): def __init__(self, weight, bias=None): super().__init__() self.weight = nn.Parameter(weight.detach().clone().contiguous(), requires_grad=False) if bias is not None: self.bias = nn.Parameter(bias.detach().clone().contiguous(), requires_grad=False) class FlowLMProgram(nn.Module): """forward(embeds[1,S,512], input_pos[1] i64, noise[1,32]) -> latent[1,32], eos_logit[1,1], next_embeds[1,1,512]. Writes the S positions [input_pos, input_pos + S) of the in-program KV cache (llama sdpa_with_kv_cache, causal), then runs the head on the last position: eos_logit = out_eos(h), latent = noise + flow_net(h, s=0, t=1, noise) and next_embeds = input_linear(latent) + cond, the embedding of the next step's input. Prompt calls (voice, text) ignore the outputs.""" def __init__(self, w: PhononWeights, num_speakers="1", duration_delta=0.0, attn="sdpa_kv"): super().__init__() from executorch.extension.llm.custom_ops import custom_ops # noqa: F401 f = w.flow_lm self.attn = attn self.layers = nn.ModuleList() for L in f.transformer.layers: m = nn.Module() m.norm1, m.norm2 = L.norm1, L.norm2 m.in_proj = _LinP(_permuted_in_proj(L.self_attn.in_proj.weight)) m.out_proj, m.linear1, m.linear2 = L.self_attn.out_proj, L.linear1, L.linear2 self.layers.append(m) self.out_norm, self.out_eos, self.input_linear = f.out_norm, f.out_eos, f.input_linear self.fn = f.flow_net with torch.no_grad(): self.register_buffer("cond", summed_conditions(f, num_speakers, duration_delta).clone(), persistent=False) self.register_buffer("t_comb", lsd_time(f.flow_net).clone(), persistent=False) self.register_buffer("mean_w", torch.full((1, D), 1.0 / D), persistent=False) cos, sin = rope_tables(MAX_CTX) self.register_buffer("rope_cos", cos, persistent=False) self.register_buffer("rope_sin", sin, persistent=False) for i in range(FLOW_LAYERS): self.register_buffer(f"k_cache_{i}", torch.zeros(1, MAX_CTX, H, HD), persistent=False) self.register_buffer(f"v_cache_{i}", torch.zeros(1, MAX_CTX, H, HD), persistent=False) def reset(self): for i in range(FLOW_LAYERS): getattr(self, f"k_cache_{i}").zero_() getattr(self, f"v_cache_{i}").zero_() def forward(self, embeds, input_pos, noise): S = embeds.shape[1] p = input_pos[0].item() torch._check_is_size(p) torch._check(p + S <= MAX_CTX) cos = self.rope_cos.narrow(0, p, S) sin = self.rope_sin.narrow(0, p, S) x = embeds for i, L in enumerate(self.layers): h = layer_norm_dyn(L.norm1, x, 1e-5, self.mean_w) qkv = lin3(L.in_proj, h) q = qkv[..., :D].reshape(1, S, H, HD) k = qkv[..., D : 2 * D].reshape(1, S, H, HD) v = qkv[..., 2 * D :].reshape(1, S, H, HD) q, k = _rope_hf(q, k, cos, sin) kc, vc = getattr(self, f"k_cache_{i}"), getattr(self, f"v_cache_{i}") o = torch.ops.llama.sdpa_with_kv_cache(q, k, v, kc, vc, p, S, None, 0.0, True, None) x = x + lin3(L.out_proj, o.reshape(1, S, D)) h = layer_norm_dyn(L.norm2, x, 1e-5, self.mean_w) x = x + lin3(L.linear2, gelu(lin3(L.linear1, h))) h = layer_norm(self.out_norm, x[:, -1, :], 1e-5) eos = lin3(self.out_eos, h) latent = noise + flow_net(self.fn, h, noise, self.t_comb) nxt = lin3(self.input_linear, latent).reshape(1, 1, D) + self.cond return latent, eos, nxt MIMI_STATE = [ ("upsample_partial", (1, D, UPS)), ("k0", (1, H, MIMI_CONTEXT, HD)), ("v0", (1, H, MIMI_CONTEXT, HD)), ("k1", (1, H, MIMI_CONTEXT, HD)), ("v1", (1, H, MIMI_CONTEXT, HD)), ("kv_seen", (1, 1, 1, MIMI_CONTEXT)), ("rope_offset", (1,)), ("init_conv_prev", (1, 512, 6)), ("up1_partial", (1, 256, 6)), ("res1_prev", (1, 256, 2)), ("up2_partial", (1, 128, 5)), ("res2_prev", (1, 128, 2)), ("up3_partial", (1, 64, 4)), ("res3_prev", (1, 64, 2)), ("final_conv_prev", (1, 64, 2)), ] def mimi_zero_state(): return [torch.zeros(s) for _, s in MIMI_STATE] def _convtr(x, w, b, s, groups=1): """ConvTranspose1d as a 4-D (H = 1) conv_transpose2d: the public runtime's 3-D convolution has no transposed path, its 4-D one does (with groups).""" y = F.conv_transpose2d(x.unsqueeze(2), w.unsqueeze(2), b, stride=(1, s), groups=groups) return y.squeeze(2) def _convtr_step(x, w, b, partial, s, groups=1): y = _convtr(x, w, b, s, groups) pt = partial.shape[2] n = y.shape[2] head = y[:, :, :pt] + partial out = torch.cat([head, y[:, :, pt : n - pt]], 2) tail = y[:, :, n - pt :] if b is not None: tail = tail - b.view(1, -1, 1) return out, tail def _conv_step(m, x, prev): xx = torch.cat([prev, x], 2) y = F.conv1d(xx, m.conv.weight, m.conv.bias) return y, xx[:, :, xx.shape[2] - prev.shape[2] :] class MimiStepProgram(nn.Module): """forward(latent[1,1,32], *state) -> pcm[1,1,1920], *new_state (MIMI_STATE order). One latent frame per call, exactly the runtime's streaming decoder: every piece of streaming state (conv tails, transposed-conv partial sums, the 2-layer transformer's last 250 keys and values, which of them are filled, and the RoPE offset) is an explicit input and output. The first call takes all-zero state.""" def __init__(self, w: PhononWeights): super().__init__() m = w.mimi self.emb_std = nn.Parameter(w.flow_lm.emb_std.detach().clone().view(1, 1, LDIM), requires_grad=False) self.emb_mean = nn.Parameter(w.flow_lm.emb_mean.detach().clone().view(1, 1, LDIM), requires_grad=False) self.qproj = _LinP(m.quantizer.output_proj.weight.view(D, LDIM)) self.up_w = nn.Parameter(m.upsample.convtr.convtr.weight.detach().clone(), requires_grad=False) self.layers = nn.ModuleList() for L in m.decoder_transformer.transformer.layers: q = nn.Module() q.norm1, q.norm2 = L.norm1, L.norm2 q.in_proj = _LinP(_permuted_in_proj(L.self_attn.in_proj.weight)) q.out_proj, q.linear1, q.linear2 = L.self_attn.out_proj, L.linear1, L.linear2 q.ls1 = nn.Parameter(L.layer_scale_1.scale.detach().clone(), requires_grad=False) q.ls2 = nn.Parameter(L.layer_scale_2.scale.detach().clone(), requires_grad=False) self.layers.append(q) self.dec = m.decoder.model self.register_buffer("inv_freq", inv_freq().view(1, HD // 2), persistent=False) self.register_buffer("steps", torch.arange(UPS, dtype=torch.float32).view(UPS, 1), persistent=False) i = torch.arange(UPS)[:, None] j = torch.arange(MIMI_CONTEXT + UPS)[None, :] band = torch.where((j >= i) & (j <= i + MIMI_CONTEXT), 0.0, NEG) self.register_buffer("band", band.view(1, 1, UPS, MIMI_CONTEXT + UPS), persistent=False) self.register_buffer("ones_seen", torch.ones(1, 1, 1, UPS), persistent=False) def _attn(self, L, x, kc, vc, seen, cos, sin): T = UPS h = layer_norm(L.norm1, x, 1e-5) qkv = lin(L.in_proj, h) q = qkv[..., :D].reshape(1, T, H, HD) k = qkv[..., D : 2 * D].reshape(1, T, H, HD) v = qkv[..., 2 * D :].reshape(1, T, H, HD) rh = lambda t: torch.cat([-t[..., HD // 2 :], t[..., : HD // 2]], -1) # noqa: E731 q = q * cos + rh(q) * sin k = k * cos + rh(k) * sin q = q.permute(0, 2, 1, 3) # [1, H, T, HD] k = k.permute(0, 2, 1, 3) v = v.permute(0, 2, 1, 3) kk = torch.cat([kc, k], 2) # [1, H, 266, HD] vv = torch.cat([vc, v], 2) seen_all = torch.cat([seen, self.ones_seen], 3) mask = self.band + (seen_all - 1.0) * 1.0e30 sc = torch.bmm(q.reshape(H, T, HD), kk.reshape(H, MIMI_CONTEXT + T, HD).transpose(1, 2)) * (HD**-0.5) pr = torch.softmax(sc.reshape(1, H, T, MIMI_CONTEXT + T) + mask, -1) o = torch.bmm(pr.reshape(H, T, MIMI_CONTEXT + T), vv.reshape(H, MIMI_CONTEXT + T, HD)) o = o.reshape(1, H, T, HD).permute(0, 2, 1, 3).reshape(1, T, D) x = x + L.ls1 * lin(L.out_proj, o) h = layer_norm(L.norm2, x, 1e-5) x = x + L.ls2 * lin(L.linear2, gelu(lin(L.linear1, h))) return x, kk[:, :, UPS:], vv[:, :, UPS:], seen_all[..., UPS:] def _res(self, blk, x, prev): v, prev = _conv_step(blk.block[1], elu(x), prev) v = F.conv1d(elu(v), blk.block[3].conv.weight, blk.block[3].conv.bias) return x + v, prev def forward(self, latent, up_p, k0, v0, k1, v1, seen, off, c0, u1, r1, u2, r2, u3, r3, cf): x = latent * self.emb_std + self.emb_mean # [1, 1, 32] x = lin(self.qproj, x).reshape(1, D, 1) x, up_p = _convtr_step(x, self.up_w, None, up_p, UPS, groups=D) # [1, 512, 16] pos = self.steps + off # [16, 1] ang = pos * self.inv_freq # [16, 32] cos = torch.cat([torch.cos(ang), torch.cos(ang)], -1).reshape(1, UPS, 1, HD) sin = torch.cat([torch.sin(ang), torch.sin(ang)], -1).reshape(1, UPS, 1, HD) x = x.permute(0, 2, 1) # [1, 16, 512] x, k0, v0, seen0 = self._attn(self.layers[0], x, k0, v0, seen, cos, sin) x, k1, v1, _ = self._attn(self.layers[1], x, k1, v1, seen, cos, sin) x = x.permute(0, 2, 1) # [1, 512, 16] m = self.dec x, c0 = _conv_step(m[0], x, c0) x, u1 = _convtr_step(elu(x), m[2].convtr.weight, m[2].convtr.bias, u1, 6) x, r1 = self._res(m[3], x, r1) x, u2 = _convtr_step(elu(x), m[5].convtr.weight, m[5].convtr.bias, u2, 5) x, r2 = self._res(m[6], x, r2) x, u3 = _convtr_step(elu(x), m[8].convtr.weight, m[8].convtr.bias, u3, 4) x, r3 = self._res(m[9], x, r3) pcm, cf = _conv_step(m[11], elu(x), cf) return pcm, up_p, k0, v0, k1, v1, seen0, off + float(UPS), c0, u1, r1, u2, r2, u3, r3, cf