python / climberhillclimb

Climber

Climber holds the modules you picked.

A climber is a set of modules, and its arguments come in the order one step of a search runs them. The selector picks the node the attempt starts from, or none. The policy names the operator for it. The operator makes the attempt, reading what memory has to say. A tune trial of the result may follow. Climber takes each of these as a name, a class or an instance, and anything you leave out keeps its default.

from hillclimb import Climber
from hillclimb.memory import FilesMemory
from hillclimb.operators import Debug, Draft, Improve
from hillclimb.policies import Greedy
from hillclimb.selectors import MapElites

climber = Climber(
    selector_policy=MapElites(num_islands=2, num_drafts=3),   # which node, or none
    operator_policy=Greedy(tune_budget=4),                    # which operator for it
    operators=[Draft(retrieval=False), Debug(), Improve()],   # how the attempt is made
    tuner="optuna",                                  # how a tune trial picks values
    memory=FilesMemory(max_cards=1),                 # what the operator is told
    name="mine",                                     # just a label
)

Each kind of module has a module to import it from: hillclimb.policies, hillclimb.selectors, hillclimb.operators, hillclimb.tuners, hillclimb.memory and hillclimb.loops.

partwhat does it decide?comes with
selector_policywhich node the next attempt starts from, or none. It also carries the schedule: a failing tip first, roots until num_drafts, the final ensemble windowselectors.Best, selectors.MapElites
operator_policywhich operator to apply to what the selector chosepolicies.Greedy
operatorshow an attempt is made. The built-in ones are classes like any you write, so [Draft(), Debug(), Improve()] is the usual spelling; a name works toooperators.Draft, Debug, Improve, Ensemble
tunerhow parameter values are chosentuners.RandomSearch, tuners.Optuna
memorywhat a search reads from earlier searches and leaves for later onesmemory.FilesMemory, memory.NoMemory
loopuse this instead of a selector and a policy when the climber wants to own the whole looploops.GepaLoop
params, selector_paramsthe policy's knobs and the selector's, as dicts
promptsa folder of prompt templates that replace the built-in ones

An instance is shorthand for a class plus its parameters. Best(num_drafts=3) means selector_policy: best with selector_params: {num_drafts: 3}, and every search builds a fresh instance from that. If you misspell a knob you find out right away. Greedy(tune_budge=3) raises a TypeError before anything has been spent, and so does a schedule knob on the wrong module: Greedy(num_drafts=3) tells you it belongs to the selector.

Presets

You can also start from a preset. Its name stands for a whole block, and the other arguments go on top.

Climber("greedy")
Climber("openevolve", params={"num_drafts": 5})   # greedy over a MAP-Elites archive
Climber(loop="gepa")                              # needs pip install 'hillclimb[gepa]'

The block behind it

A climber built in Python and a climber: block in YAML are the same thing, and you can move between them.

climber.to_spec().block()        # the block as a dict
climber.write("climber.yaml")    # the block as a file, ready for a run spec or runs/config.yaml
climber.sha256                   # its identity, covering the block and every file it reaches

Your own classes are written down in the most portable form available. A registry name if there is one, then module:Class if the module can be imported, otherwise its_file.py:Class. That last form means a search will import your script again, so keep the code that starts a search under if __name__ == "__main__":. A class that only exists in the running process, say in a notebook cell, still works with search and start. The climber is then not portable, and to_spec(), hillclimb resume and detached engines tell you so instead of trying.

What you can do with one

climber.search(problem, ...)run one search and keep the result. See Search
climber.start(problem, ...)open the same search and step through it. See Stepping
climber.best, climber.history, climber.solution, climber.to_frame()what the search found. See Results
climber.to_spec(), climber.write(path)get the block
climber.name, climber.sha256, climber.portableits label, its identity, and whether it can be written down