Download src/v20_compact_universal_u2.py from cpchoi/modular-arithmetic-challenge: direct link, hf CLI and curl.
- Browser
- Download file 6.5 kB
-
https://huggingface.co/cpchoi/modular-arithmetic-challenge/resolve/main/src/v20_compact_universal_u2.py
- Command line
-
hf download hf://cpchoi/modular-arithmetic-challenge/src/v20_compact_universal_u2.py
-
curl -L -o v20_compact_universal_u2.py https://huggingface.co/cpchoi/modular-arithmetic-challenge/resolve/main/src/v20_compact_universal_u2.py
6.5 kB
| """V20-sized learned controller for the two-program U2 arithmetic board.""" | |
| from __future__ import annotations | |
| from collections.abc import Sequence | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from src.self_clocked_parallel_montgomery_teacher_student import select_candidate | |
| from src.universal_self_clocked_u1 import OP_COUNT, PHASE_COUNT, UniversalU1Memory | |
| from src.universal_self_clocked_u2 import ( | |
| ALGORITHM_COUNT, | |
| UniversalSelfClockedU2, | |
| ) | |
| class CompactMixedAlgorithmSelfClockTeacher(nn.Module): | |
| """Learn a finite program using compact scalar clock transitions.""" | |
| def __init__(self, *, maximum_width: int = 512, maximum_clock: int = 2048) -> None: | |
| super().__init__() | |
| if maximum_width < 1: | |
| raise ValueError("maximum_width must be positive") | |
| if maximum_clock < 4 * maximum_width: | |
| raise ValueError("maximum_clock must cover four bits per base-16 digit") | |
| self.maximum_width = int(maximum_width) | |
| self.maximum_clock = int(maximum_clock) | |
| self.operation = nn.Embedding(PHASE_COUNT * 2, OP_COUNT) | |
| self.initial_phase = nn.Embedding(ALGORITHM_COUNT, PHASE_COUNT) | |
| self.initial_clock_affine = nn.Embedding(ALGORITHM_COUNT, 2) | |
| self.decrement_clock_affine = nn.Linear(1, 1) | |
| self.terminal_clock = nn.Parameter(torch.empty(1)) | |
| self.terminal_test = nn.Linear(1, 2) | |
| self.next_phase = nn.Embedding(PHASE_COUNT * OP_COUNT, PHASE_COUNT) | |
| for parameter in self.parameters(): | |
| nn.init.normal_(parameter, std=0.02) | |
| self.config = { | |
| "maximum_width": self.maximum_width, | |
| "maximum_clock": self.maximum_clock, | |
| } | |
| def parameter_count(self) -> int: | |
| return sum(parameter.numel() for parameter in self.parameters()) | |
| def _validate_algorithms(self, algorithm_ids: torch.Tensor) -> None: | |
| if algorithm_ids.ndim != 1: | |
| raise ValueError("algorithm_ids must be a one-dimensional batch") | |
| if bool( | |
| ((algorithm_ids < 0) | (algorithm_ids >= ALGORITHM_COUNT)).any().item() | |
| ): | |
| raise ValueError("algorithm id is outside the learned instruction domain") | |
| def initial_clock_values( | |
| self, algorithm_ids: torch.Tensor, widths: torch.Tensor | |
| ) -> torch.Tensor: | |
| self._validate_algorithms(algorithm_ids) | |
| if algorithm_ids.shape != widths.shape: | |
| raise ValueError("algorithm ids and widths must align") | |
| scale_bias = self.initial_clock_affine(algorithm_ids) | |
| return scale_bias[:, 0] * widths.to(scale_bias.dtype) + scale_bias[:, 1] | |
| def raw_decrement_clock_values(self, clocks: torch.Tensor) -> torch.Tensor: | |
| return self.decrement_clock_affine( | |
| clocks.to(torch.float32).unsqueeze(-1) | |
| ).squeeze(-1) | |
| def decrement_clock_values(self, clocks: torch.Tensor) -> torch.Tensor: | |
| return F.relu(self.raw_decrement_clock_values(clocks)) | |
| def terminal_test_logits(self, clocks: torch.Tensor) -> torch.Tensor: | |
| return self.terminal_test(clocks.to(torch.float32).unsqueeze(-1)) | |
| def _discrete_clock(self, values: torch.Tensor) -> torch.Tensor: | |
| return values.round().clamp(0, self.maximum_clock).to(torch.long) | |
| def start( | |
| self, width: int, algorithm_ids: torch.Tensor | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| if not 1 <= int(width) <= self.maximum_width: | |
| raise ValueError("width exceeds the learned controller domain") | |
| self._validate_algorithms(algorithm_ids) | |
| widths = torch.full_like(algorithm_ids, int(width)) | |
| phase = self.initial_phase(algorithm_ids).argmax(-1) | |
| clock = self._discrete_clock( | |
| self.initial_clock_values(algorithm_ids, widths) | |
| ) | |
| return phase, clock | |
| def operation_logits( | |
| self, phases: torch.Tensor, clocks: torch.Tensor | |
| ) -> torch.Tensor: | |
| clock_class = self.terminal_test_logits(clocks).argmax(-1) | |
| return self.operation(phases * 2 + clock_class) | |
| def instruction(self, phase: torch.Tensor, clock: torch.Tensor) -> torch.Tensor: | |
| return self.operation_logits(phase, clock).argmax(-1) | |
| def advance( | |
| self, | |
| phase: torch.Tensor, | |
| clock: torch.Tensor, | |
| operation: torch.Tensor, | |
| algorithm_ids: torch.Tensor, | |
| *, | |
| width: int, | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| widths = torch.full_like(clock, int(width)) | |
| row_clock = self._discrete_clock(self.decrement_clock_values(clock)) | |
| reset_clock = self._discrete_clock( | |
| self.initial_clock_values(algorithm_ids, widths) | |
| ) | |
| terminal_clock = self._discrete_clock(self.terminal_clock).expand_as(clock) | |
| next_clock = select_candidate( | |
| ( | |
| row_clock, | |
| reset_clock, | |
| terminal_clock, | |
| row_clock, | |
| terminal_clock, | |
| clock, | |
| ), | |
| operation, | |
| ) | |
| next_phase = self.next_phase(phase * OP_COUNT + operation).argmax(-1) | |
| return next_phase, next_clock | |
| class V20CompactUniversalSelfClockedU2(UniversalSelfClockedU2): | |
| """Use the U2 arithmetic board with a compact learned controller.""" | |
| def __init__( | |
| self, | |
| *, | |
| reducer: dict, | |
| controller: dict, | |
| mac: dict, | |
| subtract: dict, | |
| factor: dict, | |
| cancellation_teacher: dict, | |
| prime_two_router: dict, | |
| ) -> None: | |
| super().__init__( | |
| reducer=reducer, | |
| controller=controller, | |
| mac=mac, | |
| subtract=subtract, | |
| factor=factor, | |
| cancellation_teacher=cancellation_teacher, | |
| prime_two_router=prime_two_router, | |
| ) | |
| self.controller = CompactMixedAlgorithmSelfClockTeacher(**controller) | |
| self.config = { | |
| "reducer": dict(reducer), | |
| "controller": dict(controller), | |
| "mac": dict(mac), | |
| "subtract": dict(subtract), | |
| "factor": dict(factor), | |
| "cancellation_teacher": dict(cancellation_teacher), | |
| "prime_two_router": dict(prime_two_router), | |
| } | |
| def _controller_instruction(self, memory: UniversalU1Memory) -> torch.Tensor: | |
| return self.controller.instruction(memory.phase, memory.clock) | |
| __all__ = [ | |
| "CompactMixedAlgorithmSelfClockTeacher", | |
| "V20CompactUniversalSelfClockedU2", | |
| ] | |