Skip to content

Commit 8a6c97c

Browse files
committed
add pre eval callback and stringify user info for logging
Signed-off-by: Grossberger Lukas (CR/AIR2.2) <Lukas.Grossberger@de.bosch.com>
1 parent c1ff8d9 commit 8a6c97c

File tree

2 files changed

+43
-11
lines changed

2 files changed

+43
-11
lines changed

blackboxopt/optimization_loops/sequential.py

+27-11
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ def run_optimization_loop(
2626
optimizer: Union[SingleObjectiveOptimizer, MultiObjectiveOptimizer],
2727
evaluation_function: Callable[[EvaluationSpecification], Evaluation],
2828
timeout_s: float = float("inf"),
29-
max_evaluations: int = None,
29+
max_evaluations: Optional[int] = None,
3030
catch_exceptions_from_evaluation_function: bool = False,
31+
pre_evaluation_callback: Optional[Callable[[EvaluationSpecification], Any]] = None,
3132
post_evaluation_callback: Optional[Callable[[Evaluation], Any]] = None,
32-
logger: logging.Logger = None,
33+
logger: Optional[logging.Logger] = None,
3334
) -> List[Evaluation]:
3435
"""Convenience wrapper for an optimization loop that sequentially fetches evaluation
3536
specifications until a given timeout or maximum number of evaluations is reached.
@@ -53,9 +54,12 @@ def run_optimization_loop(
5354
exception raised by the evaluation function or instead store their stack
5455
trace in the evaluation's `stacktrace` attribute. Set to True if there are
5556
spurious errors due to e.g. numerical instability that should not halt the
56-
optimization loop.
57+
optimization loop. For more details, see the wrapper that is used internally
58+
`blackboxopt.optimization_loops.utils.evaluation_function_wrapper`
59+
pre_evaluation_callback: Reference to a callable that is invoked before each
60+
evaluation and takes a `blackboxopt.EvaluationSpecification` as an argument.
5761
post_evaluation_callback: Reference to a callable that is invoked after each
58-
evaluation and takes a `blackboxopt.Evaluation` as its argument.
62+
evaluation and takes a `blackboxopt.Evaluation` as an argument.
5963
logger: The logger to use for logging progress. Default: `blackboxopt.logger`
6064
6165
Returns:
@@ -82,10 +86,13 @@ def run_optimization_loop(
8286

8387
try:
8488
evaluation_specification = optimizer.generate_evaluation_specification()
89+
8590
logger.info(
86-
"The optimizer proposed a specification for evaluation:\n"
87-
+ f"{json.dumps(evaluation_specification.to_dict(), indent=2)}"
91+
"The optimizer proposed the following evaluation specification:\n%s",
92+
json.dumps(evaluation_specification.to_dict(), indent=2),
8893
)
94+
if pre_evaluation_callback is not None:
95+
pre_evaluation_callback(evaluation_specification)
8996

9097
evaluation = evaluation_function_wrapper(
9198
evaluation_function=evaluation_function,
@@ -94,16 +101,25 @@ def run_optimization_loop(
94101
objectives=objectives,
95102
catch_exceptions_from_evaluation_function=catch_exceptions_from_evaluation_function,
96103
)
104+
97105
logger.info(
98-
"Reporting the result from the evaluation function to the optimizer:\n"
99-
+ f"{json.dumps(evaluation.to_dict(), indent=2)}"
106+
"Reporting the following evaluation result to the optimizer:\n%s",
107+
json.dumps(
108+
{
109+
# Stringify the user_info because it is not guaranteed to be
110+
# json serializable
111+
k: str(v) if k == "user_info" else v
112+
for k, v in evaluation.to_dict().items()
113+
},
114+
indent=2,
115+
),
100116
)
101-
optimizer.report(evaluation)
102-
evaluations.append(evaluation)
103-
104117
if post_evaluation_callback is not None:
105118
post_evaluation_callback(evaluation)
106119

120+
optimizer.report(evaluation)
121+
evaluations.append(evaluation)
122+
107123
except OptimizerNotReady:
108124
logger.info("Optimizer is not ready yet, retrying in two seconds")
109125
time.sleep(2)

tests/optimization_loops/sequential_test.py

+16
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,19 @@ def callback(e: Evaluation):
5858

5959
assert len(evaluations) == len(evaluations_from_callback)
6060
assert evaluations == evaluations_from_callback
61+
62+
63+
def test_pre_evaluation_callback():
64+
eval_specs_from_callback = []
65+
66+
def callback(e: Evaluation):
67+
eval_specs_from_callback.append(e)
68+
69+
evaluations = run_optimization_loop(
70+
RandomSearch(testing.SPACE, [Objective("loss", False)], max_steps=10),
71+
lambda e: e.create_evaluation(objectives={"loss": 0.0}),
72+
pre_evaluation_callback=callback,
73+
)
74+
75+
assert len(evaluations) == len(eval_specs_from_callback)
76+
assert [e.get_specification() for e in evaluations] == eval_specs_from_callback

0 commit comments

Comments
 (0)