41 lines
1.5 KiB
Python
41 lines
1.5 KiB
Python
"""Adaptive compute allocation under the 30-minute wall clock.
|
|
|
|
Symbolic solvers cost ~0; the budget really governs LLM calls (CEGIS rounds
|
|
and best-of-N). Strategy: reserve a safety margin, spread the rest over the
|
|
LLM-needing puzzles, and degrade rounds/N as the clock runs down — never let
|
|
the tail of the test set hit the fallback because the head overspent.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
|
|
|
|
class Budget:
|
|
def __init__(self, total_seconds: float = 1620.0, safety_margin: float = 120.0):
|
|
self.start = time.monotonic()
|
|
self.total = total_seconds
|
|
self.safety = safety_margin
|
|
|
|
def elapsed(self) -> float:
|
|
return time.monotonic() - self.start
|
|
|
|
def remaining(self) -> float:
|
|
return self.total - self.safety - self.elapsed()
|
|
|
|
def exhausted(self) -> bool:
|
|
return self.remaining() <= 0
|
|
|
|
def cegis_rounds(self, puzzles_left: int, seconds_per_round: float = 12.0) -> int:
|
|
"""How many CEGIS refinement rounds this puzzle can afford, assuming
|
|
every remaining puzzle needs at least one proposal."""
|
|
if puzzles_left <= 0:
|
|
puzzles_left = 1
|
|
per_puzzle = self.remaining() / puzzles_left
|
|
rounds = int(per_puzzle / seconds_per_round) - 1 # -1 for the initial proposal
|
|
return max(0, min(rounds, 3))
|
|
|
|
def allow_llm(self, puzzles_left: int, seconds_per_call: float = 12.0) -> bool:
|
|
"""False once only fallback-speed work fits for the remaining set."""
|
|
return self.remaining() > puzzles_left * 0.2 + seconds_per_call
|