2
2
3
3
import numpy as np
4
4
from .target_space import TargetSpace
5
+ from warnings import warn
5
6
6
7
7
8
class DomainTransformer ():
@@ -36,7 +37,10 @@ def __init__(
36
37
self .minimum_window_value = minimum_window
37
38
38
39
def initialize (self , target_space : TargetSpace ) -> None :
39
- """Initialize all of the parameters"""
40
+ """Initialize all of the parameters.
41
+ """
42
+
43
+ # Set the original bounds
40
44
self .original_bounds = np .copy (target_space .bounds )
41
45
self .bounds = [self .original_bounds ]
42
46
@@ -47,6 +51,7 @@ def initialize(self, target_space: TargetSpace) -> None:
47
51
else :
48
52
self .minimum_window = [self .minimum_window_value ] * len (target_space .bounds )
49
53
54
+ # Set initial values
50
55
self .previous_optimal = np .mean (target_space .bounds , axis = 1 )
51
56
self .current_optimal = np .mean (target_space .bounds , axis = 1 )
52
57
self .r = target_space .bounds [:, 1 ] - target_space .bounds [:, 0 ]
@@ -72,14 +77,13 @@ def initialize(self, target_space: TargetSpace) -> None:
72
77
self ._window_bounds_compatibility (self .original_bounds )
73
78
74
79
def _update (self , target_space : TargetSpace ) -> None :
75
-
80
+ """ Updates contraction rate, window size, and window center.
81
+ """
76
82
# setting the previous
77
83
self .previous_optimal = self .current_optimal
78
84
self .previous_d = self .current_d
79
-
80
- self .current_optimal = target_space .params [
81
- np .argmax (target_space .target )
82
- ]
85
+
86
+ self .current_optimal = target_space .params_to_array (target_space .max ()['params' ])
83
87
84
88
self .current_d = 2.0 * (self .current_optimal -
85
89
self .previous_optimal ) / self .r
@@ -97,32 +101,84 @@ def _update(self, target_space: TargetSpace) -> None:
97
101
self .r = self .contraction_rate * self .r
98
102
99
103
def _trim (self , new_bounds : np .array , global_bounds : np .array ) -> np .array :
100
- for i , variable in enumerate (new_bounds ):
101
- if variable [0 ] < global_bounds [i , 0 ]:
102
- variable [0 ] = global_bounds [i , 0 ]
103
- if variable [1 ] > global_bounds [i , 1 ]:
104
- variable [1 ] = global_bounds [i , 1 ]
105
- for i , entry in enumerate (new_bounds ):
106
- if entry [0 ] > entry [1 ]:
107
- new_bounds [i , 0 ] = entry [1 ]
108
- new_bounds [i , 1 ] = entry [0 ]
109
- window_width = abs (entry [0 ] - entry [1 ])
110
- if window_width < self .minimum_window [i ]:
111
- dw = (self .minimum_window [i ] - window_width ) / 2.0
112
- left_expansion_space = abs (global_bounds [i , 0 ] - entry [0 ]) # should be non-positive
113
- right_expansion_space = abs (global_bounds [i , 1 ] - entry [1 ]) # should be non-negative
114
- # conservative
115
- dw_l = min (dw , left_expansion_space )
116
- dw_r = min (dw , right_expansion_space )
117
- # this crawls towards the edge
118
- ddw_r = dw_r + max (dw - dw_l , 0 )
119
- ddw_l = dw_l + max (dw - dw_r , 0 )
120
- new_bounds [i , 0 ] -= ddw_l
121
- new_bounds [i , 1 ] += ddw_r
104
+ """
105
+ Adjust the new_bounds and verify that they adhere to global_bounds and minimum_window.
106
+
107
+ Parameters:
108
+ -----------
109
+ new_bounds : np.array
110
+ The proposed new_bounds that (may) need adjustment.
111
+
112
+ global_bounds : np.array
113
+ The maximum allowable bounds for each parameter.
114
+
115
+ Returns:
116
+ --------
117
+ new_bounds : np.array
118
+ The adjusted bounds after enforcing constraints.
119
+ """
120
+
121
+ #sort bounds
122
+ new_bounds = np .sort (new_bounds )
123
+
124
+ # Validate each parameter's bounds against the global_bounds
125
+ for i , pbounds in enumerate (new_bounds ):
126
+ # If the one of the bounds is outside the global bounds, reset the bound to the global bound
127
+ # This is expected to happen when the window is near the global bounds, no warning is issued
128
+ if (pbounds [0 ] < global_bounds [i , 0 ]):
129
+ pbounds [0 ] = global_bounds [i , 0 ]
130
+
131
+ if (pbounds [1 ] > global_bounds [i , 1 ]):
132
+ pbounds [1 ] = global_bounds [i , 1 ]
133
+
134
+ # If a lower bound is greater than the associated global upper bound, reset it to the global lower bound
135
+ if (pbounds [0 ] > global_bounds [i , 1 ]):
136
+ pbounds [0 ] = global_bounds [i , 0 ]
137
+ warn ("\n Domain Reduction Warning:\n " +
138
+ "A parameter's lower bound is greater than the global upper bound." +
139
+ "The offensive boundary has been reset." +
140
+ "Be cautious of subsequent reductions." , stacklevel = 2 )
141
+
142
+ # If an upper bound is less than the associated global lower bound, reset it to the global upper bound
143
+ if (pbounds [1 ] < global_bounds [i , 0 ]):
144
+ pbounds [1 ] = global_bounds [i , 1 ]
145
+ warn ("\n Domain Reduction Warning:\n " +
146
+ "A parameter's lower bound is greater than the global upper bound." +
147
+ "The offensive boundary has been reset." +
148
+ "Be cautious of subsequent reductions." , stacklevel = 2 )
149
+
150
+ # Adjust new_bounds to ensure they respect the minimum window width for each parameter
151
+ for i , pbounds in enumerate (new_bounds ):
152
+ current_window_width = abs (pbounds [0 ] - pbounds [1 ])
153
+
154
+ # If the window width is less than the minimum allowable width, adjust it
155
+ # Note that when minimum_window < width of the global bounds one side always has more space than required
156
+ if current_window_width < self .minimum_window [i ]:
157
+ width_deficit = (self .minimum_window [i ] - current_window_width ) / 2.0
158
+ available_left_space = abs (global_bounds [i , 0 ] - pbounds [0 ])
159
+ available_right_space = abs (global_bounds [i , 1 ] - pbounds [1 ])
160
+
161
+ # determine how much to expand on the left and right
162
+ expand_left = min (width_deficit , available_left_space )
163
+ expand_right = min (width_deficit , available_right_space )
164
+
165
+ # calculate the deficit on each side
166
+ expand_left_deficit = width_deficit - expand_left
167
+ expand_right_deficit = width_deficit - expand_right
168
+
169
+ # shift the deficit to the side with more space
170
+ adjust_left = expand_left + max (expand_right_deficit , 0 )
171
+ adjust_right = expand_right + max (expand_left_deficit , 0 )
172
+
173
+ # adjust the bounds
174
+ pbounds [0 ] -= adjust_left
175
+ pbounds [1 ] += adjust_right
176
+
122
177
return new_bounds
123
178
124
179
def _window_bounds_compatibility (self , global_bounds : np .array ) -> bool :
125
- """Checks if global bounds are compatible with the minimum window sizes."""
180
+ """Checks if global bounds are compatible with the minimum window sizes.
181
+ """
126
182
for i , entry in enumerate (global_bounds ):
127
183
global_window_width = abs (entry [1 ] - entry [0 ])
128
184
if global_window_width < self .minimum_window [i ]:
@@ -133,7 +189,8 @@ def _create_bounds(self, parameters: dict, bounds: np.array) -> dict:
133
189
return {param : bounds [i , :] for i , param in enumerate (parameters )}
134
190
135
191
def transform (self , target_space : TargetSpace ) -> dict :
136
-
192
+ """Reduces the bounds of the target space.
193
+ """
137
194
self ._update (target_space )
138
195
139
196
new_bounds = np .array (
@@ -143,6 +200,6 @@ def transform(self, target_space: TargetSpace) -> dict:
143
200
]
144
201
).T
145
202
146
- self ._trim (new_bounds , self .original_bounds )
203
+ new_bounds = self ._trim (new_bounds , self .original_bounds )
147
204
self .bounds .append (new_bounds )
148
205
return self ._create_bounds (target_space .keys , new_bounds )
0 commit comments