Download scripts/recurrent_qwen/partitioned.py from mukunds/loopformer: direct link, hf CLI and curl.
- Browser
- Download file 9.1 kB
-
https://huggingface.co/mukunds/loopformer/resolve/main/scripts/recurrent_qwen/partitioned.py
- Command line
-
hf download hf://mukunds/loopformer/scripts/recurrent_qwen/partitioned.py
-
curl -L -o partitioned.py https://huggingface.co/mukunds/loopformer/resolve/main/scripts/recurrent_qwen/partitioned.py
9.1 kB
| """Inference-only causal factorization for count-dependent historical models. | |
| Identical prompt prefixes are computed once per graph, at *each* loop for | |
| full-sequence recurrence. Count-specific suffixes remain separate continuous | |
| states. Fixed-prompt recurrence retains first-pass layer K/V, replacing only | |
| the working position. No symbols, labels, depth values or stops enter R. | |
| """ | |
| from dataclasses import dataclass | |
| import torch | |
| from torch import Tensor, nn | |
| from transformers.models.qwen2.modeling_qwen2 import apply_rotary_pos_emb, eager_attention_forward | |
| from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS | |
| from .prefix import layer_compute | |
| def suffix_layer(layer: nn.Module, hidden: Tensor, embeddings: tuple[Tensor, Tensor], | |
| mask: Tensor, prefix: tuple[Tensor, Tensor], | |
| replace_positions: Tensor | None = None) -> tuple[Tensor, Tensor, Tensor]: | |
| """Queries [B,Q,H], rotary K/V prefix [B,KV,P,D]; full additive query mask.""" | |
| attention = layer.self_attn | |
| x = layer.input_layernorm(hidden) | |
| shape = (*x.shape[:-1], -1, attention.head_dim) | |
| q = attention.q_proj(x).view(shape).transpose(1, 2) | |
| k = attention.k_proj(x).view(shape).transpose(1, 2) | |
| v = attention.v_proj(x).view(shape).transpose(1, 2) | |
| q, k = apply_rotary_pos_emb(q, k, *embeddings) | |
| if replace_positions is None: | |
| k, v = torch.cat((prefix[0], k), -2), torch.cat((prefix[1], v), -2) | |
| else: | |
| # Work on fresh tensors: original fixed memory is immutable across loops. | |
| k_full, v_full = prefix[0].clone(), prefix[1].clone() | |
| rows = torch.arange(len(hidden), device=hidden.device) | |
| k_full[rows, :, replace_positions, :] = k[:, :, 0, :] | |
| v_full[rows, :, replace_positions, :] = v[:, :, 0, :] | |
| k, v = k_full, v_full | |
| interface = ALL_ATTENTION_FUNCTIONS.get_interface(attention.config._attn_implementation, eager_attention_forward) | |
| output, _ = interface(attention, q, k, v, mask, | |
| dropout=0.0, scaling=attention.scaling, sliding_window=None) | |
| output = output.reshape(*x.shape[:-1], -1).contiguous() | |
| result = hidden + attention.o_proj(output) | |
| result = result + layer.mlp(layer.post_attention_layernorm(result)) | |
| return result, k, v | |
| class PartitionedResult: | |
| """Readouts [graph*count, loop, symbol], stop logits or None, final R working.""" | |
| scores: Tensor | |
| stops: Tensor | None | |
| working: Tensor | |
| def partitioned_forward(model: nn.Module, prefix_ids: Tensor, suffix_ids: Tensor, | |
| suffix_mask: Tensor, graph_indices: Tensor, | |
| token_ids: list[int], loops: int, horizons: Tensor | None = None) -> PartitionedResult: | |
| """Exact causal factorization, modulo documented FP32 kernel roundoff. | |
| Prefix [G,P] has no padding, suffix [B,S] is right padded. Each suffix maps | |
| to one prefix via [B] graph_indices. Positions match the dense concatenation. | |
| Supports historical full_sequence/fixed_prompt models, without routed inputs, | |
| bridges, direct heads or private recurrent controllers. Never modifies model. | |
| """ | |
| if (model.training or model.router is not None or model.bridge is not None or | |
| model.state_head is not None or loops < 1): | |
| raise ValueError('Partitioned path requires an unmodified historical eval model') | |
| if prefix_ids.ndim != 2 or suffix_ids.shape != suffix_mask.shape or suffix_ids.ndim != 2: | |
| raise ValueError('Expected prefix and aligned suffix token matrices') | |
| if (not suffix_mask.any(-1).all() or not torch.equal(suffix_mask.long().cumprod(-1), suffix_mask.long()) | |
| or graph_indices.shape != (len(suffix_ids),) or | |
| (graph_indices < 0).any() or (graph_indices >= len(prefix_ids)).any()): | |
| raise ValueError('Need right-padded suffixes and valid graph indices') | |
| prefix = model.embed_tokens(prefix_ids) | |
| suffix = model.embed_tokens(suffix_ids) | |
| device, dtype = prefix.device, prefix.dtype | |
| p, s = prefix.shape[1], suffix.shape[1] | |
| pp = torch.arange(p, device=device)[None] | |
| sp = torch.arange(p, p + s, device=device)[None] | |
| pe, se = model.rotary_emb(prefix, pp), model.rotary_emb(suffix, sp) | |
| negative = torch.finfo(dtype).min | |
| pm = torch.zeros(p, p, device=device, dtype=dtype).masked_fill( | |
| torch.arange(p, device=device)[None] > torch.arange(p, device=device)[:, None], negative)[None, None] | |
| keep = torch.cat((torch.ones(len(suffix), p, device=device, dtype=torch.bool), suffix_mask.bool()), -1) | |
| allowed = (torch.arange(p + s, device=device)[None] <= sp[0, :, None])[None] & keep[:, None, :] | |
| sm = torch.zeros_like(allowed, dtype=dtype).masked_fill(~allowed, negative)[:, None] | |
| ends = suffix_mask.sum(-1).long() - 1 | |
| rows = torch.arange(len(suffix), device=device) | |
| am = sm[rows, :, ends, :].unsqueeze(2) | |
| ae = tuple(e.expand(len(suffix), -1, -1)[rows, ends, :].unsqueeze(1) for e in se) | |
| def block(layers, ph, sh, record=False): | |
| memory = [] | |
| for layer in layers: | |
| ph, k, v = layer_compute(layer, ph, pe, pm) | |
| sh, keys, values = suffix_layer(layer, sh, se, sm, | |
| (k.index_select(0, graph_indices), v.index_select(0, graph_indices))) | |
| if record: | |
| memory.append((keys, values)) | |
| return ph, sh, memory | |
| def working_block(layers, state, memory): | |
| for layer, kv in zip(layers, memory, strict=True): | |
| state, _, _ = suffix_layer(layer, state, ae, am, kv, ends + p) | |
| return state | |
| prefix, suffix, _ = block(model.prelude.layers, prefix, suffix) | |
| fixed = model.recurrence_mode == 'fixed_prompt' | |
| scores, stops = [], [] | |
| original_batch = len(suffix_ids) | |
| active_rows = torch.arange(original_batch, device=device) | |
| crossed = torch.zeros(original_batch, device=device, dtype=torch.bool) | |
| if horizons is not None and (horizons.shape != (original_batch,) or (horizons < 1).any() or (horizons > loops).any()): | |
| raise ValueError('Requested readout horizons must fit the loop cap') | |
| r_memory = c_memory = None | |
| working = None | |
| selected = model.lm_head.weight[token_ids] | |
| for loop in range(loops): | |
| if fixed and loop: | |
| working = working_block(model.recurrent.layers, working, r_memory) | |
| decoded = working_block(model.coda.layers, working, c_memory) | |
| else: | |
| prefix, suffix, r_memory = block(model.recurrent.layers, prefix, suffix, fixed) | |
| working = suffix[rows, ends, :].unsqueeze(1) | |
| _, decoded_suffix, c_memory = block(model.coda.layers, prefix, suffix, fixed) | |
| decoded = decoded_suffix[rows, ends, :].unsqueeze(1) | |
| score = nn.functional.linear(model.norm(decoded[:, 0]), selected).float() | |
| if not torch.isfinite(score).all(): | |
| raise FloatingPointError('Nonfinite partitioned readout') | |
| full_score = score.new_zeros(original_batch, score.shape[-1]) | |
| full_score[active_rows] = score | |
| scores.append(full_score) | |
| if model.completion_head is not None: | |
| stop = model.completion_head(working[:, 0]).float() | |
| if not torch.isfinite(stop).all(): | |
| raise FloatingPointError('Nonfinite partitioned completion head') | |
| full_stop = stop.new_full((original_batch,), float('-inf')) | |
| full_stop[active_rows] = stop | |
| stops.append(full_stop) | |
| crossed[active_rows] |= stop >= 0 | |
| if horizons is not None and loop + 1 < loops: | |
| need = horizons[active_rows] > loop + 1 | |
| if model.completion_head is not None: | |
| need |= ~crossed[active_rows] | |
| # A CUDA boolean index discovers its output shape on the host. | |
| # Discover the same ordered rows once, then reuse them for every | |
| # state/cache tensor instead of repeating that synchronization. | |
| kept = need.nonzero().flatten() | |
| if not kept.numel(): | |
| for _ in range(loop + 1, loops): | |
| scores.append(full_score.new_zeros(full_score.shape)) | |
| if stops: stops.append(full_stop.new_full(full_stop.shape, float('-inf'))) | |
| break | |
| active_rows = active_rows.index_select(0, kept) | |
| graph_indices = graph_indices.index_select(0, kept) | |
| suffix = suffix.index_select(0, kept) | |
| working = working.index_select(0, kept) | |
| ends = ends.index_select(0, kept) | |
| rows = torch.arange(len(active_rows), device=device) | |
| sm = sm.index_select(0, kept); am = am.index_select(0, kept) | |
| ae = tuple(e.index_select(0, kept) for e in ae) | |
| if fixed: | |
| r_memory = [(key.index_select(0, kept), value.index_select(0, kept)) for key, value in r_memory] | |
| c_memory = [(key.index_select(0, kept), value.index_select(0, kept)) for key, value in c_memory] | |
| return PartitionedResult(torch.stack(scores, 1), torch.stack(stops, 1) if stops else None, working[:, 0]) | |