cpchoi's picture
Upload validated compact auto-routed U5d
6c3cf9d verified
Raw History Blame Contribute Delete
3.54 kB
"""Official interface for the compact auto-routed U5d candidate."""
from __future__ import annotations
from pathlib import Path
from typing import Sequence
import torch
from modchallenge.interface.base_model import ModularMultiplicationModel
from src.adaptive_algorithm_router_u3 import RawInputAlgorithmRouter
from src.compact_auto_routed_short_u5d import (
CompactAutoRoutedShortU5d,
CompactSparseUniversalShortU5d,
)
MAXIMUM_WIDTH = 512
def _hex_digits(value: str) -> list[int]:
number = int(value)
if number < 0:
raise ValueError("challenge arguments must be non-negative")
width = max(1, (number.bit_length() + 3) // 4)
return [
(number >> (4 * position)) & 15
for position in range(width - 1, -1, -1)
]
class CompactAutoRoutedShortU5dSubmission(ModularMultiplicationModel):
"""Learned raw routing into compact Montgomery and short-Horner programs."""
def __init__(self) -> None:
self.model: CompactAutoRoutedShortU5d | None = None
self.device: torch.device | None = None
def load(self, model_dir: str) -> None:
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
if self.device.type == "cpu":
torch.set_num_threads(4)
payload = torch.load(
Path(model_dir) / "weights.pt",
map_location=self.device,
weights_only=True,
)
executor = CompactSparseUniversalShortU5d(
**payload["model_config"]["executor"]
).to(self.device)
router = RawInputAlgorithmRouter(
**payload["model_config"]["router"]
).to(self.device)
self.model = CompactAutoRoutedShortU5d(
executor=executor, router=router
).to(self.device)
result = self.model.load_state_dict(payload["state_dict"], strict=True)
if result.missing_keys or result.unexpected_keys:
raise RuntimeError("compact U5d checkpoint did not load strictly")
self.model.eval()
def preprocess_a(self, a: str) -> list[int]:
return _hex_digits(a)
def preprocess_b(self, b: str) -> list[int]:
return _hex_digits(b)
def preprocess_p(self, p: str) -> list[int]:
return _hex_digits(p)
@torch.inference_mode()
def predict_digits(self, a_enc, b_enc, p_enc) -> list[int]:
return self.predict_digits_batch([(a_enc, b_enc, p_enc)])[0]
@torch.inference_mode()
def predict_digits_batch(self, inputs: Sequence[tuple]) -> list[list[int]]:
if self.model is None:
raise RuntimeError("model has not been loaded")
answers: list[list[int] | None] = [None] * len(inputs)
supported = [
index
for index, (_a, _b, modulus) in enumerate(inputs)
if len(modulus) <= MAXIMUM_WIDTH
]
if supported:
prediction = self.model.predict_encoded(
[inputs[index][0] for index in supported],
[inputs[index][1] for index in supported],
[inputs[index][2] for index in supported],
)
for index, row in zip(supported, prediction.answers.tolist()):
answers[index] = [int(digit) for digit in row]
for index, answer in enumerate(answers):
if answer is None:
answers[index] = [0]
return [answer for answer in answers if answer is not None]
def max_batch_size(self) -> int:
return 20
__all__ = ["CompactAutoRoutedShortU5dSubmission"]