forked from bayesian-optimization/BayesianOptimization
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_logger.py
More file actions
346 lines (264 loc) · 11.1 KB
/
test_logger.py
File metadata and controls
346 lines (264 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
from __future__ import annotations
import io
from unittest.mock import patch
from colorama import Fore
from bayes_opt import BayesianOptimization
from bayes_opt.logger import ScreenLogger
def target_func(**kwargs):
"""Simple target function for testing."""
return sum(kwargs.values())
PBOUNDS = {"p1": (0, 10), "p2": (0, 10)}
def test_initialization():
"""Test logger initialization with default and custom parameters."""
# Default parameters
logger = ScreenLogger()
assert logger.verbose == 2
assert not logger.is_constrained
# Custom parameters
logger = ScreenLogger(verbose=0, is_constrained=True)
assert logger.verbose == 0
assert logger.is_constrained
def test_verbose_property():
"""Test the verbose property getter and setter."""
logger = ScreenLogger(verbose=1)
assert logger.verbose == 1
logger.verbose = 0
assert logger.verbose == 0
logger.verbose = 2
assert logger.verbose == 2
def test_is_constrained_property():
"""Test the is_constrained property getter."""
logger = ScreenLogger(is_constrained=False)
assert not logger.is_constrained
logger = ScreenLogger(is_constrained=True)
assert logger.is_constrained
def test_format_number():
"""Test the _format_number method."""
logger = ScreenLogger()
# Test integer formatting
assert len(logger._format_number(42)) == logger._default_cell_size
# Test float formatting with precision
float_str = logger._format_number(3.14159)
assert len(float_str) == logger._default_cell_size
assert "3.14" in float_str # default precision is 4
# Test long integer truncation
long_int = 12345678901234
formatted = logger._format_number(long_int)
assert len(formatted) == logger._default_cell_size
assert formatted == "1.234e+13"
# Test long float truncation
long_float = 1234.5678901234
formatted = logger._format_number(long_float)
assert len(formatted) == logger._default_cell_size
assert formatted == "1234.5678"
# Test negative long float truncation
long_float = -1234.5678901234
formatted = logger._format_number(long_float)
assert len(formatted) == logger._default_cell_size
assert formatted == "-1234.567"
# Test scientific notation truncation
sci_float = 12345678901234.5678901234
formatted = logger._format_number(sci_float)
assert len(formatted) == logger._default_cell_size
assert formatted == "1.234e+13"
sci_float = 1.11111111e-5
formatted = logger._format_number(sci_float)
assert formatted == "1.111e-05"
sci_float_neg = -1.11111111e-5
formatted = logger._format_number(sci_float_neg)
assert formatted == "-1.11e-05"
sci_float = -12345678901234.5678901234
formatted = logger._format_number(sci_float)
assert len(formatted) == logger._default_cell_size
assert formatted == "-1.23e+13"
# Test long scientific notation truncation
sci_float = -12345678901234.534e132
formatted = logger._format_number(sci_float)
assert len(formatted) == logger._default_cell_size
assert formatted == "-1.2e+145"
def test_format_bool():
"""Test the _format_bool method."""
logger = ScreenLogger()
# Test True formatting
true_str = logger._format_bool(True)
assert len(true_str) == logger._default_cell_size
assert "True" in true_str
# Test False formatting
false_str = logger._format_bool(False)
assert len(false_str) == logger._default_cell_size
assert "False" in false_str
# Test with small cell size
small_cell_logger = ScreenLogger()
small_cell_logger._default_cell_size = 3
assert small_cell_logger._format_bool(True) == "T "
assert small_cell_logger._format_bool(False) == "F "
def test_format_str():
"""Test the _format_str method."""
logger = ScreenLogger()
# Test normal string
normal_str = logger._format_str("test")
assert len(normal_str) == logger._default_cell_size
assert "test" in normal_str
# Test long string truncation
long_str = "this_is_a_very_long_string_that_should_be_truncated"
formatted = logger._format_str(long_str)
assert len(formatted) == logger._default_cell_size
assert "..." in formatted
def test_step():
"""Test the _print_step method."""
optimizer = BayesianOptimization(target_func, PBOUNDS, random_state=1)
logger = ScreenLogger()
# Register a point so we have something to log
optimizer.register(params={"p1": 1.5, "p2": 2.5}, target=4.0)
# Test default color
step_str = logger._print_step(
optimizer._space.keys, optimizer._space.res()[-1], optimizer._space.params_config
)
assert "|" in step_str
assert "1" in step_str # iteration
assert "4.0" in step_str # target value
# Test with custom color
custom_color = Fore.RED
step_str_colored = logger._print_step(
optimizer._space.keys, optimizer._space.res()[-1], optimizer._space.params_config, color=custom_color
)
assert custom_color in step_str_colored
def test_print_header():
"""Test the _print_header method."""
optimizer = BayesianOptimization(target_func, PBOUNDS, random_state=1)
logger = ScreenLogger()
header_str = logger._print_header(optimizer._space.keys)
# Check if header contains expected column names
assert "iter" in header_str
assert "target" in header_str
assert "p1" in header_str
assert "p2" in header_str
# Check if divider line is included
assert "-" * 10 in header_str
# Check with constrained logger
constrained_logger = ScreenLogger(is_constrained=True)
constrained_header = constrained_logger._print_header(optimizer._space.keys)
assert "allowed" in constrained_header
def test_is_new_max():
"""Test the _is_new_max method."""
optimizer = BayesianOptimization(target_func, PBOUNDS, random_state=1)
logger = ScreenLogger()
# No observations yet
assert not logger._is_new_max(None)
# Add first observation
optimizer.register(params={"p1": 1, "p2": 2}, target=3)
current_max = optimizer.max
logger._is_new_max(current_max)
assert not logger._is_new_max(current_max)
# Add lower observation (not a new max)
optimizer.register(params={"p1": 0.5, "p2": 1}, target=1.5)
assert not logger._is_new_max(optimizer.max)
# Add higher observation (new max)
optimizer.register(params={"p1": 2, "p2": 2}, target=4)
assert logger._is_new_max(optimizer.max)
def test_update_tracker():
"""Test the _update_tracker method."""
optimizer = BayesianOptimization(target_func, PBOUNDS, random_state=1)
logger = ScreenLogger()
# Initial state
assert logger._iterations == 0
assert logger._previous_max is None
# Update with first observation
optimizer.register(params={"p1": 1, "p2": 2}, target=3)
logger._update_tracker(optimizer.max)
assert logger._iterations == 1
assert logger._previous_max == 3
assert logger._previous_max_params == {"p1": 1, "p2": 2}
# Update with lower observation
optimizer.register(params={"p1": 0.5, "p2": 1}, target=1.5)
logger._update_tracker(optimizer.max)
assert logger._iterations == 2
assert logger._previous_max == 3 # Unchanged
assert logger._previous_max_params == {"p1": 1, "p2": 2} # Unchanged
# Update with higher observation
optimizer.register(params={"p1": 2, "p2": 2}, target=4)
logger._update_tracker(optimizer.max)
assert logger._iterations == 3
assert logger._previous_max == 4 # Updated
assert logger._previous_max_params == {"p1": 2, "p2": 2} # Updated
@patch("sys.stdout", new_callable=io.StringIO)
def test_log_optimization_start(mock_stdout):
"""Test the log_optimization_start method."""
optimizer = BayesianOptimization(target_func, PBOUNDS, random_state=1)
# Test with verbose=0 (should not print)
logger = ScreenLogger(verbose=0)
logger.log_optimization_start(optimizer._space.keys)
assert mock_stdout.getvalue() == ""
# Test with verbose=1 (should print)
logger.verbose = 1
logger.log_optimization_start(optimizer._space.keys)
output = mock_stdout.getvalue()
assert "iter" in output
assert "target" in output
assert "p1" in output
assert "p2" in output
@patch("sys.stdout", new_callable=io.StringIO)
def test_log_optimization_step(mock_stdout):
"""Test the log_optimization_step method."""
optimizer = BayesianOptimization(target_func, PBOUNDS, random_state=1)
logger = ScreenLogger()
# Create logger with verbose=1 specifically, as this is the only verbose level
# that doesn't print for non-max points according to the implementation:
# if self._verbose == 2 or is_new_max:
logger.verbose = 1
# Clear any output that might have happened
mock_stdout.truncate(0)
mock_stdout.seek(0)
# Register a point and log it
optimizer.register(params={"p1": 1, "p2": 2}, target=3)
current_max = optimizer.max
logger._is_new_max(current_max) # Initialize previous_max
# For a point that is not a new max with verbose=1, should not print
mock_stdout.truncate(0)
mock_stdout.seek(0)
logger.log_optimization_step(
optimizer._space.keys, optimizer._space.res()[-1], optimizer._space.params_config, optimizer.max
)
assert mock_stdout.getvalue() == "" # Nothing printed for non-max point with verbose=1
# Register a higher value, which should trigger output with verbose=1
optimizer.register(params={"p1": 2, "p2": 2}, target=4)
mock_stdout.truncate(0)
mock_stdout.seek(0)
logger.log_optimization_step(
optimizer._space.keys, optimizer._space.res()[-1], optimizer._space.params_config, optimizer.max
)
max_output = mock_stdout.getvalue()
assert max_output != "" # Something printed for new max point with verbose=1
assert "4.0" in max_output # Should show target value
# Test with verbose=2 (should print even for non-max points)
logger.verbose = 2
optimizer.register(params={"p1": 1, "p2": 1}, target=1)
mock_stdout.truncate(0)
mock_stdout.seek(0)
logger.log_optimization_step(
optimizer._space.keys, optimizer._space.res()[-1], optimizer._space.params_config, optimizer.max
)
non_max_output = mock_stdout.getvalue()
assert non_max_output != "" # Something printed for non-max point with verbose=2
assert "1.0" in non_max_output # Should show target value
@patch("sys.stdout", new_callable=io.StringIO)
def test_log_optimization_end(mock_stdout):
"""Test the log_optimization_end method."""
optimizer = BayesianOptimization(target_func, PBOUNDS, random_state=1)
# Initialize header length
logger = ScreenLogger(verbose=2)
logger.log_optimization_start(optimizer._space.keys)
# Clear previous output
mock_stdout.truncate(0)
mock_stdout.seek(0)
# Test with verbose=0 (should not print)
logger.verbose = 0
logger.log_optimization_end()
assert mock_stdout.getvalue() == ""
# Test with verbose=1 (should print)
logger.verbose = 1
mock_stdout.truncate(0)
mock_stdout.seek(0)
logger.log_optimization_end()
output = mock_stdout.getvalue()
assert "=" in output # Should show a line of equals signs