2. Selector policy
The first decision of every step, which node the next attempt starts from.
The selector policy (πsel) reads the history and answers one question: which node does the next attempt start from? Its answer is one of four things:
- a failing candidate, which gets repaired first,
- nothing, which makes the step a root step (a fresh draft),
- a scored candidate to build on,
- the top few candidates, to be combined into one in the final part of the budget.
The base class handles that schedule. A selector of your own only decides the third case, in
select:
# oldest.py
from hillclimb.sdk import Selection, SelectorPolicy
class Oldest(SelectorPolicy):
"""Build on the oldest scored candidate."""
def select(self, state, *, busy=frozenset()):
scored = state.journal.scored_candidates()
return Selection(scored[0].candidate_id, prompt_context="Oldest first.") if scored else Noneclimber: {selector_policy: oldest.py, operator_policy: greedy}select returns a Selection: the node to build on, and whatever comes with it. That can be
inspirations (other candidates copied in beside it), a paragraph for the prompt, and a note
recorded on the new candidate. None means there is nothing to build on yet, so the step is a
root step. busy holds the candidates an attempt in flight is already working on.
The schedule's settings
These belong to every selector policy and go in selector_params:
| setting | default | what it does |
|---|---|---|
num_drafts | 3 | root candidates before anything is built on |
debug | true | repair failing candidates at all |
max_debug_depth | 3 | failed fixes in a row before a failing chain is left |
ensemble | true | combine the top candidates in the final window |
ensemble_reserve_fraction | 0.2 | the final share of the budget kept for it |
ensemble_top_k | 3 | how many candidates are combined |
ensemble_max_attempts | 2 | how many combinations are tried |
The built-in ones
bestbuilds on the best scored candidate nobody is building on yet, so parallel attempts spread over the top few.map-eliteskeeps OpenEvolve's MAP-Elites archive: a population spread over feature dimensions, on islands with migration, with a parent and inspirations sampled per island. It takes OpenEvolve's settings (num_islands,feature_dimensions,population_size,random_seed, …) andnum_inspirations. The built-in feature dimensions arecomplexity,diversityandscore; any other must be a number the verifier reports beside the score.
A selector that keeps state rebuilds it from the history in sync(state), so a resumed search
makes the same choices a live one would.