Skip to content

Commit 73c5b91

Browse files
committed
add data_collect parameter and result attributes
1 parent 898337c commit 73c5b91

32 files changed

+417
-55
lines changed

src/surfaces/test_functions/_base_test_function.py

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,33 @@ class BaseTestFunction:
2020
memory : bool, default=False
2121
If True, caches evaluated positions to avoid redundant computations.
2222
The cache key is the position as a tuple of sorted parameter values.
23+
collect_data : bool, default=True
24+
If True, collects evaluation data including search_data, best_score,
25+
best_params, n_evaluations, and total_time. Set to False to disable
26+
tracking for performance-critical applications.
27+
28+
Attributes
29+
----------
30+
n_evaluations : int
31+
Number of function evaluations performed.
32+
search_data : list of dict
33+
History of all evaluations as list of dicts containing parameters and score.
34+
best_score : float or None
35+
Best score found (respects objective direction).
36+
best_params : dict or None
37+
Parameters that achieved the best score.
38+
total_time : float
39+
Cumulative time spent in function evaluations (seconds).
2340
2441
Examples
2542
--------
2643
>>> func = SphereFunction(n_dim=2)
2744
>>> func({"x0": 1.0, "x1": 2.0}) # dict input
2845
>>> func(np.array([1.0, 2.0])) # array input
2946
>>> func([1.0, 2.0]) # list input
47+
>>> func.n_evaluations # 3
48+
>>> func.best_score # best value seen
49+
>>> func.search_data # [{"x0": 1.0, "x1": 2.0, "score": 5.0}, ...]
3050
"""
3151

3252
pure_objective_function: callable
@@ -88,14 +108,22 @@ def wrapper(self, *args, **kwargs):
88108
return wrapper
89109

90110
@_create_objective_function_
91-
def __init__(self, objective="minimize", sleep=0, memory=False):
111+
def __init__(self, objective="minimize", sleep=0, memory=False, collect_data=True):
92112
if objective not in ("minimize", "maximize"):
93113
raise ValueError(f"objective must be 'minimize' or 'maximize', got '{objective}'")
94114
self.objective = objective
95115
self.sleep = sleep
96116
self.memory = memory
117+
self.collect_data = collect_data
97118
self._memory_cache: Dict[Tuple, float] = {}
98119

120+
# Data collection attributes
121+
self.n_evaluations: int = 0
122+
self.search_data: list = []
123+
self.best_score: Optional[float] = None
124+
self.best_params: Optional[Dict[str, Any]] = None
125+
self.total_time: float = 0.0
126+
99127
def _create_objective_function(self):
100128
raise NotImplementedError("'_create_objective_function' must be implemented")
101129

@@ -132,12 +160,23 @@ def __call__(
132160
if self.memory:
133161
cache_key = self._params_to_cache_key(params)
134162
if cache_key in self._memory_cache:
135-
return self._memory_cache[cache_key]
136-
result = self._evaluate(params)
163+
result = self._memory_cache[cache_key]
164+
if self.collect_data:
165+
self._record_evaluation(params, result, from_cache=True)
166+
return result
167+
168+
start_time = time.time()
169+
result = self._evaluate(params)
170+
elapsed_time = time.time() - start_time
171+
172+
if self.memory:
173+
cache_key = self._params_to_cache_key(params)
137174
self._memory_cache[cache_key] = result
138-
return result
139175

140-
return self._evaluate(params)
176+
if self.collect_data:
177+
self._record_evaluation(params, result, elapsed_time=elapsed_time)
178+
179+
return result
141180

142181
def _normalize_input(
143182
self, params: Optional[Union[Dict[str, Any], np.ndarray, list, tuple]] = None, **kwargs
@@ -165,3 +204,52 @@ def _evaluate(self, params: Dict[str, Any]) -> float:
165204
if self.objective == "maximize":
166205
return -raw_value
167206
return raw_value
207+
208+
def _record_evaluation(
209+
self,
210+
params: Dict[str, Any],
211+
score: float,
212+
elapsed_time: float = 0.0,
213+
from_cache: bool = False,
214+
) -> None:
215+
"""Record an evaluation in the search data."""
216+
self.n_evaluations += 1
217+
218+
# Record in search_data
219+
record = {**params, "score": score}
220+
self.search_data.append(record)
221+
222+
# Update timing (only for non-cached evaluations)
223+
if not from_cache:
224+
self.total_time += elapsed_time
225+
226+
# Update best score/params
227+
is_better = (
228+
self.best_score is None
229+
or (self.objective == "minimize" and score < self.best_score)
230+
or (self.objective == "maximize" and score > self.best_score)
231+
)
232+
if is_better:
233+
self.best_score = score
234+
self.best_params = params.copy()
235+
236+
def reset_data(self) -> None:
237+
"""Reset all collected evaluation data.
238+
239+
Clears n_evaluations, search_data, best_score, best_params, and total_time.
240+
Does not clear the memory cache (use reset_memory() for that).
241+
"""
242+
self.n_evaluations = 0
243+
self.search_data = []
244+
self.best_score = None
245+
self.best_params = None
246+
self.total_time = 0.0
247+
248+
def reset_memory(self) -> None:
249+
"""Clear the memory cache."""
250+
self._memory_cache = {}
251+
252+
def reset(self) -> None:
253+
"""Reset all state including collected data and memory cache."""
254+
self.reset_data()
255+
self.reset_memory()

src/surfaces/test_functions/algebraic/_base_algebraic_function.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,14 @@ def search_space(self) -> Dict[str, Any]:
4545
min_val, max_val = self.default_bounds
4646
return self._create_n_dim_search_space(min=min_val, max=max_val, size=self.default_size)
4747

48-
def __init__(self, objective: str = "minimize", sleep: float = 0, memory: bool = False):
49-
super().__init__(objective, sleep, memory)
48+
def __init__(
49+
self,
50+
objective: str = "minimize",
51+
sleep: float = 0,
52+
memory: bool = False,
53+
collect_data: bool = True,
54+
):
55+
super().__init__(objective, sleep, memory, collect_data)
5056

5157
def _create_n_dim_search_space(
5258
self,

src/surfaces/test_functions/algebraic/test_functions_1d/gramacy_and_lee_function.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ class GramacyAndLeeFunction(AlgebraicFunction):
6868
default_bounds = (0.5, 2.5)
6969
n_dim = 1
7070

71-
def __init__(self, objective="minimize", sleep=0, memory=False):
72-
super().__init__(objective, sleep, memory)
71+
def __init__(self, objective="minimize", sleep=0, memory=False, collect_data=True):
72+
super().__init__(objective, sleep, memory, collect_data)
7373
self.n_dim = 1
7474

7575
def _create_objective_function(self):

src/surfaces/test_functions/algebraic/test_functions_2d/ackley_function.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,9 @@ def __init__(
8181
objective="minimize",
8282
sleep=0,
8383
memory=False,
84+
collect_data=True,
8485
):
85-
super().__init__(objective, sleep, memory)
86+
super().__init__(objective, sleep, memory, collect_data)
8687

8788
self.n_dim = 2
8889

src/surfaces/test_functions/algebraic/test_functions_2d/beale_function.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ class BealeFunction(AlgebraicFunction):
7474
default_bounds = (-4.5, 4.5)
7575
n_dim = 2
7676

77-
def __init__(self, A=1.5, B=2.25, C=2.652, objective="minimize", sleep=0, memory=False):
78-
super().__init__(objective, sleep, memory)
77+
def __init__(self, A=1.5, B=2.25, C=2.652, objective="minimize", sleep=0, memory=False, collect_data=True):
78+
super().__init__(objective, sleep, memory, collect_data)
7979
self.n_dim = 2
8080

8181
self.A = A

src/surfaces/test_functions/algebraic/test_functions_2d/booth_function.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ class BoothFunction(AlgebraicFunction):
6363
default_bounds = (-10.0, 10.0)
6464
n_dim = 2
6565

66-
def __init__(self, objective="minimize", sleep=0, memory=False):
67-
super().__init__(objective, sleep, memory)
66+
def __init__(self, objective="minimize", sleep=0, memory=False, collect_data=True):
67+
super().__init__(objective, sleep, memory, collect_data)
6868
self.n_dim = 2
6969

7070
def _create_objective_function(self):

src/surfaces/test_functions/algebraic/test_functions_2d/bukin_function_n6.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ class BukinFunctionN6(AlgebraicFunction):
6262
default_bounds = (-8.0, 8.0)
6363
n_dim = 2
6464

65-
def __init__(self, objective="minimize", sleep=0, memory=False):
66-
super().__init__(objective, sleep, memory)
65+
def __init__(self, objective="minimize", sleep=0, memory=False, collect_data=True):
66+
super().__init__(objective, sleep, memory, collect_data)
6767
self.n_dim = 2
6868

6969
def _create_objective_function(self):

src/surfaces/test_functions/algebraic/test_functions_2d/cross_in_tray_function.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,9 @@ def __init__(
8383
objective="minimize",
8484
sleep=0,
8585
memory=False,
86+
collect_data=True,
8687
):
87-
super().__init__(objective, sleep, memory)
88+
super().__init__(objective, sleep, memory, collect_data)
8889
self.n_dim = 2
8990

9091
self.A = A

src/surfaces/test_functions/algebraic/test_functions_2d/drop_wave_function.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ class DropWaveFunction(AlgebraicFunction):
6161
default_bounds = (-5.0, 5.0)
6262
n_dim = 2
6363

64-
def __init__(self, objective="minimize", sleep=0, memory=False):
65-
super().__init__(objective, sleep, memory)
64+
def __init__(self, objective="minimize", sleep=0, memory=False, collect_data=True):
65+
super().__init__(objective, sleep, memory, collect_data)
6666
self.n_dim = 2
6767

6868
def _create_objective_function(self):

src/surfaces/test_functions/algebraic/test_functions_2d/easom_function.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ class EasomFunction(AlgebraicFunction):
7272
default_bounds = (-10.0, 10.0)
7373
n_dim = 2
7474

75-
def __init__(self, A=-1, B=1, angle=1, objective="minimize", sleep=0, memory=False):
76-
super().__init__(objective, sleep, memory)
75+
def __init__(self, A=-1, B=1, angle=1, objective="minimize", sleep=0, memory=False, collect_data=True):
76+
super().__init__(objective, sleep, memory, collect_data)
7777
self.n_dim = 2
7878

7979
self.A = A

0 commit comments

Comments
 (0)