summaryrefslogtreecommitdiff
path: root/src/model_api.py
blob: 07d5d8a627ec9c3e742e5be68e8b4e6da96fb4da (plain)
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
"""Model interface contract + build dispatch + shared greedy decode."""
import torch
import torch.nn as nn

from src.config import Config
from src.data import pad_inputs


class PrimeModel(nn.Module):
    """Contract: forward(x, y_in) -> {"logits": (B,T_out,vocab), "halt_steps": (B,) or None}."""

    def forward(self, x, y_in):
        raise NotImplementedError

    def param_count(self) -> int:
        return sum(p.numel() for p in self.parameters())


def build_model(cfg: Config) -> PrimeModel:
    if cfg.model == "rnn":
        from src.models.rnn import TiedRNN
        return TiedRNN(cfg)
    if cfg.model == "transformer":
        from src.models.transformer import TransformerBaseline
        return TransformerBaseline(cfg)
    raise ValueError(f"unknown model: {cfg.model}")


@torch.no_grad()
def greedy_decode(model: PrimeModel, x: torch.Tensor, cfg: Config, max_len: int | None = None) -> torch.Tensor:
    """Autoregressive greedy decode of output digits. Returns (B, max_len) tokens (BOS stripped).

    Inputs are LEFT-padded to the global layout so positions match training exactly
    (batch/singleton invariance — codex BLOCKER fix)."""
    try:
        device = next(model.parameters()).device
    except StopIteration:
        device = x.device
    if x.device != device:
        x = x.to(device)
    x = pad_inputs(x, cfg)
    from src.data import _global_lengths
    _, out_max = _global_lengths(cfg)
    max_len = max_len or max(getattr(cfg, "max_out_len", 6), out_max)
    B = x.shape[0]
    y_in = torch.full((B, 1), cfg.eos_id, dtype=torch.long, device=device)
    for _ in range(max_len):
        out = model(x, y_in)
        nxt = out["logits"][:, -1].argmax(-1)
        y_in = torch.cat([y_in, nxt[:, None]], dim=1)
    return y_in[:, 1:]