Skip to content

Commit a24bf9e

Browse files
committed
add search history class
1 parent 7dd89f8 commit a24bf9e

1 file changed

Lines changed: 163 additions & 0 deletions

File tree

src/hyperactive/base/_history.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
"""Search history for tracking optimization trials."""
2+
3+
# copyright: hyperactive developers, MIT License (see LICENSE file)
4+
5+
from __future__ import annotations
6+
7+
8+
class SearchHistory:
9+
"""Container for tracking optimization trial history.
10+
11+
This class collects data from each evaluation during optimization runs.
12+
History accumulates across multiple optimization runs on the same experiment.
13+
14+
Attributes
15+
----------
16+
trials : list[dict]
17+
List of all recorded trials. Each trial is a dict containing:
18+
- iteration: int, global iteration index
19+
- run_id: int, which optimization run (0-indexed)
20+
- params: dict, the evaluated parameters
21+
- score: float, the evaluation score (raw, not sign-corrected)
22+
- metadata: dict, additional metadata from the experiment
23+
- eval_time: float, evaluation time in seconds
24+
"""
25+
26+
def __init__(self):
27+
self._trials: list[dict] = []
28+
self._current_run_id: int = 0
29+
30+
def record(
31+
self,
32+
params: dict,
33+
score: float,
34+
metadata: dict | None,
35+
eval_time: float,
36+
) -> None:
37+
"""Record a single trial.
38+
39+
Parameters
40+
----------
41+
params : dict
42+
The evaluated parameters.
43+
score : float
44+
The evaluation score (raw, not sign-corrected).
45+
metadata : dict or None
46+
Additional metadata from the experiment.
47+
eval_time : float
48+
Evaluation time in seconds.
49+
"""
50+
self._trials.append({
51+
"iteration": len(self._trials),
52+
"run_id": self._current_run_id,
53+
"params": dict(params),
54+
"score": float(score),
55+
"metadata": dict(metadata) if metadata else {},
56+
"eval_time": float(eval_time),
57+
})
58+
59+
def new_run(self) -> None:
60+
"""Signal the start of a new optimization run.
61+
62+
Increments the run_id counter. Subsequent trials will be tagged
63+
with the new run_id.
64+
"""
65+
self._current_run_id += 1
66+
67+
def clear(self) -> None:
68+
"""Clear all history data and reset run counter."""
69+
self._trials = []
70+
self._current_run_id = 0
71+
72+
@property
73+
def history(self) -> list[dict]:
74+
"""Return all recorded evaluations as a list.
75+
76+
Returns
77+
-------
78+
list[dict]
79+
List of all evaluations. Each entry contains iteration, run_id,
80+
params, score, metadata, and eval_time.
81+
"""
82+
return self._trials
83+
84+
@property
85+
def n_trials(self) -> int:
86+
"""Return the total number of recorded trials.
87+
88+
Returns
89+
-------
90+
int
91+
Number of trials across all runs.
92+
"""
93+
return len(self._trials)
94+
95+
@property
96+
def n_runs(self) -> int:
97+
"""Return the number of optimization runs.
98+
99+
Returns
100+
-------
101+
int
102+
Number of runs (0 if no trials recorded yet).
103+
"""
104+
return self._current_run_id + 1
105+
106+
@property
107+
def best_trial(self) -> dict | None:
108+
"""Return the trial with the highest score.
109+
110+
Returns
111+
-------
112+
dict or None
113+
The trial dict with the highest score, or None if no trials.
114+
"""
115+
if not self._trials:
116+
return None
117+
return max(self._trials, key=lambda t: t["score"])
118+
119+
@property
120+
def best_score(self) -> float | None:
121+
"""Return the highest score across all trials.
122+
123+
Returns
124+
-------
125+
float or None
126+
The highest score, or None if no trials.
127+
"""
128+
best = self.best_trial
129+
return best["score"] if best else None
130+
131+
@property
132+
def best_params(self) -> dict | None:
133+
"""Return the parameters of the best trial.
134+
135+
Returns
136+
-------
137+
dict or None
138+
Parameters of the trial with highest score, or None if no trials.
139+
"""
140+
best = self.best_trial
141+
return best["params"] if best else None
142+
143+
def get_run(self, run_id: int) -> list[dict]:
144+
"""Return all trials from a specific run.
145+
146+
Parameters
147+
----------
148+
run_id : int
149+
The run identifier (0-indexed).
150+
151+
Returns
152+
-------
153+
list[dict]
154+
List of trials from the specified run.
155+
"""
156+
return [t for t in self._trials if t["run_id"] == run_id]
157+
158+
def __len__(self) -> int:
159+
"""Return the number of trials."""
160+
return len(self._trials)
161+
162+
def __repr__(self) -> str:
163+
return f"SearchHistory(n_trials={self.n_trials}, n_runs={self.n_runs})"

0 commit comments

Comments
 (0)