File size: 13,887 Bytes
9b38930
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d6969f5
 
 
 
9b38930
 
 
 
 
 
883ced7
deccf4b
883ced7
 
deccf4b
883ced7
d6969f5
883ced7
9b38930
 
 
 
 
 
deccf4b
883ced7
 
 
deccf4b
9b38930
 
 
 
 
 
 
 
 
 
 
 
 
 
a554d13
 
 
9b38930
 
 
 
a554d13
 
 
 
 
 
9b38930
a554d13
 
d6969f5
9b38930
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
deccf4b
d6969f5
9b38930
 
 
 
deccf4b
9b38930
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
deccf4b
9b38930
 
883ced7
9b38930
 
 
 
 
 
 
 
 
 
 
 
 
 
 
883ced7
9b38930
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a2873bf
deccf4b
883ced7
9b38930
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
"""Optimized drop-in patch for summerAIO/diffusion-lfm2.5-24b-a2b (modeling_diffusion_lfm.py).

What changes vs the original sampler
-------------------------------------
1. Prefix reuse (exact): the prefix is causal and never attends to the canvas, so its
   per-layer hidden states are computed ONCE per block instead of once per denoising step.
   Each step then runs the MoE FFN / conv only on the canvas tokens. Attention layers still
   see the full prefix K/V, conv layers see the last few prefix tokens.
   -> a self-test compares against the original forward and falls back automatically if
      the internals don't match (e.g. a different transformers version).
2. Attention mask / rotary built once per block, not every step.
3. No GPU->CPU sync inside the step loop (commit count is computed arithmetically).
4. Bug fixes: eos_token_id may be a list; pad_token_id may be None; prompt is no longer
   silently cut to its last 256 tokens (prefix window is configurable, and the prompt
   itself is always kept); EOS inside a partially-committed canvas stops early.
5. Optional quality knobs (off by default = original behaviour):
   repetition_penalty, top_k / top_p, and remask_low_conf (re-masks the weakest committed
   tokens once before the final step).

Usage
-----
    from diffusion_lfm_fast import from_pretrained
    model = from_pretrained(repo, torch_dtype="bfloat16", device_map="auto")
    model.self_test(tokenizer)          # optional, also runs automatically on first generate
    tokens, stats = model.generate(prompt, max_new_tokens=1024, block_size=256, steps=8)
"""
import torch
from torch import nn


def _as_list(x):
    if x is None:
        return []
    return list(x) if isinstance(x, (list, tuple)) else [x]


class DiffusionLFM(nn.Module):
    def __init__(self, causal_model, mask_embedding):
        super().__init__()
        self.model = causal_model
        self.mask_embedding = nn.Parameter(mask_embedding.detach().clone(), requires_grad=False)
        for layer in self.model.model.layers:
            if layer.is_attention_layer:
                layer.self_attn.is_causal = False
        cfg = self.model.config
        self.eos_ids = set(_as_list(cfg.eos_token_id))
        pad = getattr(cfg, "pad_token_id", None)
        self.pad_id = pad if pad is not None else (min(self.eos_ids) if self.eos_ids else 0)
        self.conv_tail = int(getattr(cfg, "conv_L_cache", 3)) + 1  # causal conv context
        self.use_prefix_cache = None  # decided by self_test()

    @property
    def causal_model(self):
        return self.model

    # ------------------------------------------------------------------ masks
    @staticmethod
    def _mask(length, p, device, dtype):
        q = torch.arange(length, device=device)[:, None]
        k = torch.arange(length, device=device)[None, :]
        allowed = torch.where(q < p, k <= q, torch.ones_like(k <= q))
        return torch.zeros(allowed.shape, device=device, dtype=dtype).masked_fill(
            ~allowed, torch.finfo(dtype).min)[None, None]

    def _embed(self, ids, masked):
        h = self.model.model.embed_tokens(ids)
        return torch.where(masked[..., None], self.mask_embedding.to(h.dtype), h)

    # ------------------------------------------------ original (reference) path
    def hidden(self, ids, masked, valid_or_p, prefix_lengths=None):
        """Backward compatible: hidden(ids, masked, valid, prefix_lengths) (original API)
        or hidden(ids, masked, p)."""
        m = self.model.model
        h = self._embed(ids, masked)
        pos = torch.arange(ids.shape[1], device=ids.device)[None]
        rot = m.pos_emb(h, pos)
        if prefix_lengths is None:
            p = int(valid_or_p)
            valid = torch.ones_like(ids, dtype=torch.bool)
        else:
            p = int(prefix_lengths.reshape(-1)[0])
            valid = valid_or_p.bool()
        attn = self._mask(ids.shape[1], p, ids.device, h.dtype)
        if not bool(valid.all()):
            attn = attn.masked_fill(~valid[:, None, None, :], torch.finfo(h.dtype).min)
        for layer in m.layers:
            h = layer(h, position_embeddings=rot, attention_mask=attn if layer.is_attention_layer else valid,
                      position_ids=pos, past_key_values=None)
        return m.embedding_norm(h)

    # ------------------------------------------------------ prefix-reuse path
    def _layer_parts(self, layer, x, rot, attn, ctx):
        """Run one decoder layer on canvas x, with ctx = this layer's prefix input."""
        n = x.shape[1]
        if layer.is_attention_layer:
            full = torch.cat((ctx, x), 1)
            out = layer.self_attn(hidden_states=layer.operator_norm(full),
                                  position_embeddings=rot, attention_mask=attn)
        else:
            full = torch.cat((ctx[:, -self.conv_tail:], x), 1)
            out = layer.conv(layer.operator_norm(full))
        out = out[0] if isinstance(out, tuple) else out
        h = out[:, -n:] + x
        ff = layer.feed_forward(layer.ffn_norm(h))
        ff = ff[0] if isinstance(ff, tuple) else ff
        return h + ff

    def _prefix_states(self, prefix):
        """Per-layer INPUT hidden states of the (causal) prefix, computed once."""
        m = self.model.model
        p = prefix.shape[1]
        h = m.embed_tokens(prefix)
        pos = torch.arange(p, device=prefix.device)[None]
        rot = m.pos_emb(h, pos)
        attn = self._mask(p, p, prefix.device, h.dtype)
        valid = torch.ones_like(prefix, dtype=torch.bool)
        states = []
        for layer in m.layers:
            states.append(h)
            h = layer(h, position_embeddings=rot, attention_mask=attn if layer.is_attention_layer else valid,
                      position_ids=pos, past_key_values=None)
        return states

    def _canvas_hidden(self, canvas_ids, masked, states, rot_canvas, rot_full, attn):
        m = self.model.model
        h = self._embed(canvas_ids, masked)
        for layer, ctx in zip(m.layers, states):
            h = self._layer_parts(layer, h, rot_full, attn, ctx)
        return m.embedding_norm(h)

    # ------------------------------------------------------------- self test
    @torch.inference_mode()
    def self_test(self, tokenizer=None, p=48, n=32, tol=5e-2):
        dev = self.model.device
        vocab = self.model.config.vocab_size
        g = torch.Generator(device="cpu").manual_seed(0)
        ids = torch.randint(100, min(vocab, 30000), (1, p + n), generator=g).to(dev)
        masked = torch.zeros_like(ids, dtype=torch.bool)
        masked[:, p + n // 2:] = True
        try:
            ref = self.hidden(ids, masked, p)[:, p:]
            got = self._fast_block_hidden(ids[:, :p], ids[:, p:], masked[:, p:])
            diff = (ref.float() - got.float()).abs().max().item()
            scale = ref.float().abs().max().item() + 1e-6
            self.use_prefix_cache = diff / scale < tol
            print(f"[diffusion_lfm_fast] prefix-reuse rel-diff={diff/scale:.2e} -> "
                  f"{'ENABLED' if self.use_prefix_cache else 'DISABLED (fallback)'}")
        except Exception as e:  # internals differ from what we expect
            self.use_prefix_cache = False
            print(f"[diffusion_lfm_fast] prefix-reuse unavailable ({type(e).__name__}: {e}); using reference path")
        return self.use_prefix_cache

    def _fast_block_hidden(self, prefix, canvas_ids, masked):
        """Helper used by self_test: one forward through the fast path."""
        m = self.model.model
        p, n = prefix.shape[1], canvas_ids.shape[1]
        states = self._prefix_states(prefix)
        dummy = m.embed_tokens(canvas_ids)
        rot_full = m.pos_emb(dummy, torch.arange(p + n, device=prefix.device)[None])
        attn = self._mask(p + n, p, prefix.device, dummy.dtype)[:, :, p:]
        return self._canvas_hidden(canvas_ids, masked, states, None, rot_full, attn)

    # ------------------------------------------------------------- sampling
    def _pick(self, logits, temperature, top_k, top_p):
        if not temperature:
            lp = logits.log_softmax(-1)
            conf, tok = lp.max(-1)
            return tok, conf
        logits = logits / temperature
        if top_k:
            kth = logits.topk(top_k, -1).values[..., -1:]
            logits = logits.masked_fill(logits < kth, -torch.inf)
        if top_p and top_p < 1.0:
            s, idx = logits.sort(-1, descending=True)
            cum = s.softmax(-1).cumsum(-1)
            s = s.masked_fill(cum - s.softmax(-1) > top_p, -torch.inf)
            logits = torch.full_like(logits, -torch.inf).scatter(-1, idx, s)
        probs = logits.softmax(-1)
        tok = torch.multinomial(probs, 1).squeeze(-1)
        return tok, probs.gather(-1, tok[:, None]).squeeze(-1).clamp_min(1e-12).log()

    @torch.inference_mode()
    def generate(self, prompt_ids, *, max_new_tokens=128, block_size=256, steps=3,
                 temperature=0.0, top_k=0, top_p=1.0, repetition_penalty=1.0,
                 prefix_window=256, remask_low_conf=0.0):
        if prompt_ids.ndim != 2 or prompt_ids.shape[0] != 1 or min(max_new_tokens, block_size, steps) < 1:
            raise ValueError("one prompt and positive generation settings required")
        if self.use_prefix_cache is None:
            self.self_test()
        m, dev = self.model.model, prompt_ids.device
        generated, calls = [], 0
        eos_t = torch.tensor(sorted(self.eos_ids) or [-1], device=dev)
        done = False

        while len(generated) < max_new_tokens and not done:
            count = min(block_size, max_new_tokens - len(generated))
            hist = prompt_ids.new_tensor([generated[-prefix_window:]]) if generated else prompt_ids[:, :0]
            prefix = torch.cat((prompt_ids, hist), 1)
            if prefix.shape[1] > prompt_ids.shape[1] + prefix_window:  # keep prompt, window history
                prefix = torch.cat((prompt_ids, hist[:, -prefix_window:]), 1)
            p = prefix.shape[1]

            canvas = prefix.new_full((1, count), self.pad_id)
            masked = torch.ones_like(canvas, dtype=torch.bool)

            # --- built once per block
            if self.use_prefix_cache:
                states = self._prefix_states(prefix)
                dummy = m.embed_tokens(canvas)
                rot_full = m.pos_emb(dummy, torch.arange(p + count, device=dev)[None])
                attn = self._mask(p + count, p, dev, dummy.dtype)[:, :, p:]
            penalty_ids = None
            if repetition_penalty != 1.0:
                penalty_ids = torch.unique(prefix[0, -prefix_window:])

            schedule = [count * (steps - s - 1) // steps for s in range(steps)]  # remaining masked
            for s in range(steps):
                if self.use_prefix_cache:
                    h = self._canvas_hidden(canvas, masked, states, None, rot_full, attn)
                else:
                    ids = torch.cat((prefix, canvas), 1)
                    mk = torch.cat((torch.zeros_like(prefix, dtype=torch.bool), masked), 1)
                    h = self.hidden(ids, mk, p)[:, p:]
                calls += 1
                logits = self.model.lm_head(h[0]).float()           # fixed shape [count, V]
                logits[:, self.pad_id] = -torch.inf
                if penalty_ids is not None:
                    pen = torch.cat((penalty_ids, canvas[0][~masked[0]]))
                    sel = logits[:, pen]
                    logits[:, pen] = torch.where(sel > 0, sel / repetition_penalty, sel * repetition_penalty)
                tok, conf = self._pick(logits, temperature, top_k, top_p)
                conf = conf.masked_fill(~masked[0], -torch.inf)       # only fill masked slots

                if s == steps - 1:
                    canvas[0] = torch.where(masked[0], tok, canvas[0])
                    masked.zero_()
                    break
                commit = (count if s == 0 else schedule[s - 1]) - schedule[s]
                if commit > 0:
                    chosen = conf.topk(commit).indices
                    canvas[0, chosen] = tok[chosen]
                    masked[0, chosen] = False
                # optional: re-mask weakest committed tokens once, right before the last step
                if remask_low_conf and s == steps - 2:
                    with_conf = conf.clone()
                    k = int(remask_low_conf * count)
                    if k > 0:
                        committed = (~masked[0]).nonzero().squeeze(1)
                        if committed.numel() > k:
                            weakest = committed[with_conf[committed].nan_to_num(-1e9).topk(k, largest=False).indices]
                            masked[0, weakest] = True
                # early exit: an EOS has been committed and everything before it is filled
                hit = torch.isin(canvas[0], eos_t) & ~masked[0]
                if hit.any():
                    first = int(hit.nonzero()[0])
                    if not masked[0, :first].any():
                        break

            out = canvas[0].tolist()
            for i, t in enumerate(out):
                if t in self.eos_ids:
                    out, done = out[:i], True
                    break
            generated.extend(out)
        return generated[:max_new_tokens], {"forward_calls": calls, "tokens": len(generated),
                                            "prefix_cache": bool(self.use_prefix_cache)}


def from_pretrained(model_id, **kwargs):
    from safetensors.torch import load_file
    from huggingface_hub import hf_hub_download
    from transformers import AutoModelForCausalLM
    model = AutoModelForCausalLM.from_pretrained(model_id, **kwargs)
    model.eval()
    weights = load_file(hf_hub_download(model_id, "diffusion.safetensors"), device=str(model.device))
    return DiffusionLFM(model, weights["mask_embedding"]).eval()