22
33import numpy as np
44from .target_space import TargetSpace
5+ from warnings import warn
56
67
78class DomainTransformer ():
@@ -36,7 +37,10 @@ def __init__(
3637 self .minimum_window_value = minimum_window
3738
3839 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
4044 self .original_bounds = np .copy (target_space .bounds )
4145 self .bounds = [self .original_bounds ]
4246
@@ -47,6 +51,7 @@ def initialize(self, target_space: TargetSpace) -> None:
4751 else :
4852 self .minimum_window = [self .minimum_window_value ] * len (target_space .bounds )
4953
54+ # Set initial values
5055 self .previous_optimal = np .mean (target_space .bounds , axis = 1 )
5156 self .current_optimal = np .mean (target_space .bounds , axis = 1 )
5257 self .r = target_space .bounds [:, 1 ] - target_space .bounds [:, 0 ]
@@ -72,14 +77,13 @@ def initialize(self, target_space: TargetSpace) -> None:
7277 self ._window_bounds_compatibility (self .original_bounds )
7378
7479 def _update (self , target_space : TargetSpace ) -> None :
75-
80+ """ Updates contraction rate, window size, and window center.
81+ """
7682 # setting the previous
7783 self .previous_optimal = self .current_optimal
7884 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' ])
8387
8488 self .current_d = 2.0 * (self .current_optimal -
8589 self .previous_optimal ) / self .r
@@ -97,32 +101,84 @@ def _update(self, target_space: TargetSpace) -> None:
97101 self .r = self .contraction_rate * self .r
98102
99103 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+
122177 return new_bounds
123178
124179 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+ """
126182 for i , entry in enumerate (global_bounds ):
127183 global_window_width = abs (entry [1 ] - entry [0 ])
128184 if global_window_width < self .minimum_window [i ]:
@@ -133,7 +189,8 @@ def _create_bounds(self, parameters: dict, bounds: np.array) -> dict:
133189 return {param : bounds [i , :] for i , param in enumerate (parameters )}
134190
135191 def transform (self , target_space : TargetSpace ) -> dict :
136-
192+ """Reduces the bounds of the target space.
193+ """
137194 self ._update (target_space )
138195
139196 new_bounds = np .array (
@@ -143,6 +200,6 @@ def transform(self, target_space: TargetSpace) -> dict:
143200 ]
144201 ).T
145202
146- self ._trim (new_bounds , self .original_bounds )
203+ new_bounds = self ._trim (new_bounds , self .original_bounds )
147204 self .bounds .append (new_bounds )
148205 return self ._create_bounds (target_space .keys , new_bounds )
0 commit comments