climbers / operator-policyhillclimb

3. Operator policy

The second decision of every step, which operator is applied to the chosen node.

The operator policy (πop) gets what the selector policy chose and names the operator to apply to it. The base class already does the plain mapping:

the selector chosethe operator
nothingdraft, a fresh attempt
a failing candidatedebug
several candidates to combineensemble
a scored candidateimprove

So a policy of your own only has to say where it differs:

# drafts_only.py
from hillclimb.sdk import Action, OperatorPolicy


class DraftsOnly(OperatorPolicy):
    """Never improves: whatever was chosen, draft (but repair a failing candidate first)."""

    def propose(self, state, selection):
        node = state.journal.candidates[selection.target_id] if selection is not None else None
        if node is not None and node.status in ("failing", "buggy"):
            return Action(operator="debug", target_id=node.candidate_id)
        return self.draft_action(state)
hillclimb climber check --climber drafts_only.py
hillclimb run circle-packing --climber drafts_only.py

propose(state, selection) returns one Action (an operator and the candidate it starts from), or None to wait for a result in flight. selection is None for a root step. state is everything the policy sees: the history (with holdout scores hidden), what is in flight, the budget left, and which direction is better. draft_action and expand_action(state, selection, operator=...) build the usual actions, and observe(state, candidate) is called for every result.

The built-in one

greedy is the plain mapping plus one decision of its own: before improving a chosen candidate that declared tunable parameters, it spends a few tune trials on it. Its settings go in params:

settingdefaultwhat it does
tune_budget8extra trials per candidate, 0 turns tuning off
tune_gatebandwhich candidates get tuned: within the noise band of the best, only the best, or always
tune_parallel1tune trials in flight per candidate
tune_burst2tune trials between agent attempts, so tuning does not crowd out improving

Three rules

  • A policy only ever reads. It must not write to the history or to a candidate's folder.
  • Every decision must follow from the history: compute it from state, or rebuild your caches in observe. That is what makes hillclimb resume work.
  • A setting the block passes must be one the class declares in DEFAULTS, so a typo is an error before anything is spent.