initial commit

This commit is contained in:
2026-05-21 07:21:13 -04:00
commit a125b8ffda
50 changed files with 5359 additions and 0 deletions
+169
View File
@@ -0,0 +1,169 @@
from __future__ import annotations
import re
from typing import Iterable
import torch
import torch.nn.functional as F
HOLD_TOKEN_PATTERN = re.compile(r"^<([A-Z0-9_]+)_p(\d+)_(start|middle|finish|foot|unknown)>$")
def top_k_filter(logits: torch.Tensor, k: int | None) -> torch.Tensor:
if k is None or k <= 0 or k >= logits.size(-1):
return logits
values, _ = torch.topk(logits, k)
cutoff = values[:, [-1]]
return torch.where(logits < cutoff, torch.full_like(logits, -float("inf")), logits)
@torch.no_grad()
def sample_ids(
model,
prompt_ids: list[int],
device: torch.device,
max_new_tokens: int = 40,
temperature: float = 0.9,
top_k: int | None = 50,
eos_id: int | None = None,
forbidden_ids: Iterable[int] | None = None,
) -> list[int]:
model.eval()
sequence = torch.tensor([prompt_ids], dtype=torch.long, device=device)
forbidden_ids = set(forbidden_ids or [])
for _ in range(max_new_tokens):
idx_cond = sequence[:, -model.block_size :]
logits, _ = model(idx_cond)
logits = logits[:, -1, :] / max(temperature, 1e-6)
for token_id in forbidden_ids:
logits[:, int(token_id)] = -float("inf")
logits = top_k_filter(logits, top_k)
probs = F.softmax(logits, dim=-1)
next_id = torch.multinomial(probs, num_samples=1)
sequence = torch.cat([sequence, next_id], dim=1)
if eos_id is not None and int(next_id.item()) == int(eos_id):
break
return sequence[0].detach().cpu().tolist()
def prompt_tokens(board_prefix: str, angle: int, grouped_v: int) -> list[str]:
return [
"<BOS>",
f"<BOARD_{board_prefix}>",
f"<ANGLE_{int(angle)}>",
f"<GRADE_V{int(grouped_v)}>",
]
def hold_records(tokens: Iterable[str]) -> list[dict[str, object]]:
rows = []
for token in tokens:
match = HOLD_TOKEN_PATTERN.match(token)
if match is None:
continue
rows.append(
{
"board_prefix": match.group(1),
"placement_id": int(match.group(2)),
"role": match.group(3),
"token": token,
}
)
return rows
def validity_summary(tokens: Iterable[str]) -> dict[str, object]:
records = hold_records(tokens)
placements = [record["placement_id"] for record in records]
roles = [record["role"] for record in records]
prefixes = [record["board_prefix"] for record in records]
one_board_only = len(set(prefixes)) <= 1
no_duplicates = len(placements) == len(set(placements))
has_start = "start" in roles
has_finish = "finish" in roles
enough_holds = len(records) >= 3
return {
"n_hold_tokens": len(records),
"n_unique_placements": len(set(placements)),
"has_duplicate_placements": not no_duplicates,
"one_board_only": one_board_only,
"has_start": has_start,
"has_middle": "middle" in roles,
"has_finish": has_finish,
"n_start": roles.count("start"),
"n_middle": roles.count("middle"),
"n_foot": roles.count("foot"),
"n_finish": roles.count("finish"),
"basic_valid": bool(one_board_only and no_duplicates and has_start and has_finish and enough_holds),
}
def generated_tokens_to_frames(tokens: Iterable[str], role_name_to_id: dict[str, int]) -> str:
pieces = []
seen = set()
for record in hold_records(tokens):
placement_id = int(record["placement_id"])
role = str(record["role"])
if placement_id in seen or role not in role_name_to_id:
continue
seen.add(placement_id)
pieces.append(f"p{placement_id}r{int(role_name_to_id[role])}")
return "".join(pieces)
def generate_one(
model,
stoi: dict[str, int],
itos: dict[int, str],
device: torch.device,
board_prefix: str,
angle: int,
grouped_v: int,
role_name_to_id: dict[str, int],
temperature: float = 0.9,
top_k: int | None = 50,
max_new_tokens: int = 40,
) -> dict[str, object]:
unk_id = stoi["<UNK>"]
eos_id = stoi["<EOS>"]
forbidden_ids = [
stoi["<PAD>"],
stoi["<UNK>"],
stoi["<BOS>"],
stoi["<CLS>"],
stoi["<MASK>"],
]
prompt = prompt_tokens(board_prefix, angle, grouped_v)
prompt_ids = [stoi.get(token, unk_id) for token in prompt]
token_ids = sample_ids(
model=model,
prompt_ids=prompt_ids,
device=device,
max_new_tokens=max_new_tokens,
temperature=temperature,
top_k=top_k,
eos_id=eos_id,
forbidden_ids=forbidden_ids,
)
tokens = [itos.get(int(idx), "<UNK>") for idx in token_ids]
validity = validity_summary(tokens)
return {
"requested_board_prefix": board_prefix,
"requested_angle": int(angle),
"requested_grouped_v": int(grouped_v),
"temperature": float(temperature),
"top_k": None if top_k is None else int(top_k),
"tokens": tokens,
"sequence": " ".join(tokens),
"frames": generated_tokens_to_frames(tokens, role_name_to_id),
**validity,
}