Results
After search, what the search found reads off the climber. The full record is a SearchOutcome.
After search, what the search found is on the climber, the way a fitted model carries its
coefficients.
from hillclimb import Budget, Climber, Problem
from hillclimb.policies import Greedy
problem = Problem("fitness-landscape")
budget = Budget(evaluations=30)
climber = Climber(operator_policy=Greedy())
climber.search(problem, budget=budget)
climber.best # the best candidate by validation score
climber.selected # the one that ships. Usually the same, unless a holdout split disagrees
climber.candidates # every candidate, in the order they were created
climber.history # [(minutes, candidate_id, score), ...] for each time the best went up
climber.spend # evaluations, tokens, cost_usd, seconds
climber.solution # the source that ships (best/solution.py)
climber.params # the parameter values it ran with
climber.to_frame() # a pandas table, one row per candidate
climber.result # the whole SearchOutcome, for the restThese always refer to the latest search the climber ran, with start as well as search. While
a stepped search is still open they are a live reading.
The full record
climber.result is a SearchOutcome, and so is what run, climber.finish() and
climber.close() return. It is the search in a form you can read without going through runs/
by hand. It reads through the store like every other viewer, it never writes, and the journal you
get from it refuses writes too.
outcome = climber.result
outcome.state # done, parked or stopped
outcome.ref # the <run-id>/<search-id> every CLI command accepts
outcome.source("c004") # any candidate's solution.py
outcome.journal # the full journal, read only
outcome.meta # the search's record: problem, metric, climber block, budget| attribute | what it is |
|---|---|
state, error | done, parked or stopped, and the reason when it is not done |
ref, run_dir, search_dir | the <run-id>/<search-id> every CLI command accepts, and where the search lives on disk |
best | the best scored candidate by validation score |
selected | the candidate that ships. The best one, unless a holdout split ranks another higher. For a parked or stopped search, what it would ship as things stand |
candidates | every Candidate, in creation order |
history | each time the best score went up, oldest first, as (minutes, candidate_id, score). This is the staircase hillclimb chart draws |
spend | evaluations (scored attempts and tune trials the climber caused), tokens, cost_usd, seconds |
solution, params | best/solution.py, and the {name: value} dict of the selected candidate's best trial |
source(candidate_id) | a candidate's solution.py, or None if it never wrote one |
journal | the full journal, holdout scores included. Read only |
meta, status | the search's record (problem, metric, climber block, budget, tags) and the engine's last status |
to_frame() | a pandas.DataFrame indexed by candidate id with lineage, verdict, scores, cost, timing, and every number the verifier reported (x and y on the toy terrain) |
A Candidate is the journal's record of one attempt. It has candidate_id, parent_id,
operator, kind, status, val_score, holdout_score, trials, agent (tokens, cost,
duration), summary and metrics.
An earlier search
from hillclimb import open_search
open_search("20261002-221822-fitness-landscape/fitness-landscape") # by ref
open_search("20261002-221822-fitness-landscape") # a run that has one search
open_search() # the most recently active oneYou get the same kind of object for a search that already exists. A finished search is read once.
A running one, still being climbed by another process, is read again every time you look, so
state, candidates and spend keep up with it. A ref that matches nothing raises
LookupError.