From 08a5de5441edd1a460362eaba919fa570f09861d Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Mon, 14 Aug 2023 15:16:16 -0400 Subject: [PATCH 01/15] Update trim bounds in domain_reduction.py Previously, when the new upper limit was less than the original lower limit, the new_bounds could bypass the global_bounds. --- bayes_opt/domain_reduction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bayes_opt/domain_reduction.py b/bayes_opt/domain_reduction.py index f4b86ffc8..6958b80e1 100644 --- a/bayes_opt/domain_reduction.py +++ b/bayes_opt/domain_reduction.py @@ -98,9 +98,9 @@ def _update(self, target_space: TargetSpace) -> None: def _trim(self, new_bounds: np.array, global_bounds: np.array) -> np.array: for i, variable in enumerate(new_bounds): - if variable[0] < global_bounds[i, 0]: + if (variable[0] < global_bounds[i, 0] or variable[0] > global_bounds[i, 1]): variable[0] = global_bounds[i, 0] - if variable[1] > global_bounds[i, 1]: + if (variable[1] > global_bounds[i, 1] or variable[1] < global_bounds[i, 0]): variable[1] = global_bounds[i, 1] for i, entry in enumerate(new_bounds): if entry[0] > entry[1]: From cbe5c8907746709d837ed8d80766078d2493bb4a Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Mon, 4 Sep 2023 14:26:06 -0400 Subject: [PATCH 02/15] Update test_seq_domain_red.py Added test cases to catch an error when both bounds of new_bounds exceeded the global_bounds --- tests/test_seq_domain_red.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/test_seq_domain_red.py b/tests/test_seq_domain_red.py index 4b60bf049..fcff1c47b 100644 --- a/tests/test_seq_domain_red.py +++ b/tests/test_seq_domain_red.py @@ -143,4 +143,36 @@ def test_exceeded_bounds(): verbose=0, random_state=1, bounds_transformer=bounds_transformer - ) \ No newline at end of file + ) + +def test_trim_both_new_bounds_beyond_gloabal_bounds(): + """Test if the global bounds are respected when both new bounds for a given parameter + are beyond the global bounds.""" + + # initialize a bounds transformer + bounds_transformer = SequentialDomainReductionTransformer(minimum_window=10) + pbounds = {'x': (-10, 10),'y': (-10, 10)} + target_sp = TargetSpace(target_func=black_box_function, pbounds=pbounds) + bounds_transformer.initialize(target_sp) + global_bounds = np.asarray(list(pbounds.values())) + + def verify_bounds_in_range(new_bounds, global_bounds): + """Check if the new bounds are within the global bounds.""" + test = True + for i, pbounds in enumerate(new_bounds): + if (pbounds[0] < global_bounds[i, 0] or pbounds[0] > global_bounds[i, 1]): + test = False + if (pbounds[1] > global_bounds[i, 1] or pbounds[1] < global_bounds[i, 0]): + test = False + return test + + # test if both bounds for one parameter are beyond the global bounds + new_bounds = np.array( [[-50, -20], [-10, 10]] ) + trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) + assert verify_bounds_in_range(trimmed_bounds, global_bounds) + + # test if both bounds for one parameter are beyond the global bounds + # while they are out of order + new_bounds = np.array( [[-20, -50], [-10, 10]] ) + trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) + assert verify_bounds_in_range(trimmed_bounds, global_bounds) From 24775d15e5e1f665820b7b2765de8dee1778d4d7 Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Mon, 4 Sep 2023 14:29:49 -0400 Subject: [PATCH 03/15] Update domain_reduction.py _trim function now avoids an error when both bounds for a given parameter in new_bounds exceed the global_bounds --- bayes_opt/domain_reduction.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/bayes_opt/domain_reduction.py b/bayes_opt/domain_reduction.py index 6958b80e1..e86e9fb7d 100644 --- a/bayes_opt/domain_reduction.py +++ b/bayes_opt/domain_reduction.py @@ -97,15 +97,23 @@ def _update(self, target_space: TargetSpace) -> None: self.r = self.contraction_rate * self.r def _trim(self, new_bounds: np.array, global_bounds: np.array) -> np.array: - for i, variable in enumerate(new_bounds): - if (variable[0] < global_bounds[i, 0] or variable[0] > global_bounds[i, 1]): - variable[0] = global_bounds[i, 0] - if (variable[1] > global_bounds[i, 1] or variable[1] < global_bounds[i, 0]): - variable[1] = global_bounds[i, 1] + """ Performs tests and on the new_bounds to enforce global_bounds and minimum_window.""" + # sort the order of the bounds for each parameter + for i, pbounds in enumerate(new_bounds): + if pbounds[0] > pbounds[1]: + new_bounds[i, 0] = pbounds[1] + new_bounds[i, 1] = pbounds[0] + + # check if the new bounds exceed the global bounds + for i, pbounds in enumerate(new_bounds): + # check if lower bound is in range + if (pbounds[0] < global_bounds[i, 0] or pbounds[0] > global_bounds[i, 1]): + pbounds[0] = global_bounds[i, 0] + # check if upper bound is in range + if (pbounds[1] > global_bounds[i, 1] or pbounds[1] < global_bounds[i, 0]): + pbounds[1] = global_bounds[i, 1] + for i, entry in enumerate(new_bounds): - if entry[0] > entry[1]: - new_bounds[i, 0] = entry[1] - new_bounds[i, 1] = entry[0] window_width = abs(entry[0] - entry[1]) if window_width < self.minimum_window[i]: dw = (self.minimum_window[i] - window_width) / 2.0 From db7e609d4dbee9090d50356c148f2e5a229d03ba Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Mon, 4 Sep 2023 15:01:16 -0400 Subject: [PATCH 04/15] Update domain_reduction.py comments --- bayes_opt/domain_reduction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bayes_opt/domain_reduction.py b/bayes_opt/domain_reduction.py index e86e9fb7d..d99e2a184 100644 --- a/bayes_opt/domain_reduction.py +++ b/bayes_opt/domain_reduction.py @@ -106,10 +106,10 @@ def _trim(self, new_bounds: np.array, global_bounds: np.array) -> np.array: # check if the new bounds exceed the global bounds for i, pbounds in enumerate(new_bounds): - # check if lower bound is in range + # check if lower bound exceeds range if (pbounds[0] < global_bounds[i, 0] or pbounds[0] > global_bounds[i, 1]): pbounds[0] = global_bounds[i, 0] - # check if upper bound is in range + # check if upper bound exceeds range if (pbounds[1] > global_bounds[i, 1] or pbounds[1] < global_bounds[i, 0]): pbounds[1] = global_bounds[i, 1] From 216f32f9c03c6159800998d9093831f209204a64 Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Mon, 4 Sep 2023 15:31:58 -0400 Subject: [PATCH 05/15] fixed English in domain_reduction.py --- bayes_opt/domain_reduction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bayes_opt/domain_reduction.py b/bayes_opt/domain_reduction.py index d99e2a184..1a962bf47 100644 --- a/bayes_opt/domain_reduction.py +++ b/bayes_opt/domain_reduction.py @@ -97,7 +97,7 @@ def _update(self, target_space: TargetSpace) -> None: self.r = self.contraction_rate * self.r def _trim(self, new_bounds: np.array, global_bounds: np.array) -> np.array: - """ Performs tests and on the new_bounds to enforce global_bounds and minimum_window.""" + """ Tests new_bounds to enforce global_bounds and minimum_window.""" # sort the order of the bounds for each parameter for i, pbounds in enumerate(new_bounds): if pbounds[0] > pbounds[1]: From c5426fd1c6a876c91bcb41862bc001a56cf7aa26 Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Wed, 6 Sep 2023 13:56:08 -0400 Subject: [PATCH 06/15] use numpy to sort bounds, boundary exceeded warn. --- bayes_opt/domain_reduction.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/bayes_opt/domain_reduction.py b/bayes_opt/domain_reduction.py index 1a962bf47..0f5439b8e 100644 --- a/bayes_opt/domain_reduction.py +++ b/bayes_opt/domain_reduction.py @@ -2,6 +2,7 @@ import numpy as np from .target_space import TargetSpace +from warnings import warn class DomainTransformer(): @@ -99,19 +100,23 @@ def _update(self, target_space: TargetSpace) -> None: def _trim(self, new_bounds: np.array, global_bounds: np.array) -> np.array: """ Tests new_bounds to enforce global_bounds and minimum_window.""" # sort the order of the bounds for each parameter - for i, pbounds in enumerate(new_bounds): - if pbounds[0] > pbounds[1]: - new_bounds[i, 0] = pbounds[1] - new_bounds[i, 1] = pbounds[0] + new_bounds = np.sort(new_bounds) # check if the new bounds exceed the global bounds for i, pbounds in enumerate(new_bounds): # check if lower bound exceeds range if (pbounds[0] < global_bounds[i, 0] or pbounds[0] > global_bounds[i, 1]): pbounds[0] = global_bounds[i, 0] + warn("""Domain reduction warning: + A parameter's lower bound has exceeded its global boundaries. + The offensive boundary has been reset, but the optimizer may not converge.""") + # check if upper bound exceeds range if (pbounds[1] > global_bounds[i, 1] or pbounds[1] < global_bounds[i, 0]): pbounds[1] = global_bounds[i, 1] + warn("""Domain reduction warning: + A parameter's upper bound has exceeded its global boundaries. + The offensive boundary has been reset, but the optimizer may not converge.""") for i, entry in enumerate(new_bounds): window_width = abs(entry[0] - entry[1]) From 9f51495bdb6fbf11eca17ada44b9fcc985ae2685 Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Wed, 6 Sep 2023 13:58:09 -0400 Subject: [PATCH 07/15] simple sort test added --- tests/test_seq_domain_red.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_seq_domain_red.py b/tests/test_seq_domain_red.py index fcff1c47b..867ca31b9 100644 --- a/tests/test_seq_domain_red.py +++ b/tests/test_seq_domain_red.py @@ -166,6 +166,12 @@ def verify_bounds_in_range(new_bounds, global_bounds): test = False return test + + # test if the sorting of the bounds is correct + new_bounds = np.array( [[5, -5], [-10, 10]] ) + trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) + assert (trimmed_bounds == np.array( [[-5, 5], [-10, 10]] )).all() + # test if both bounds for one parameter are beyond the global bounds new_bounds = np.array( [[-50, -20], [-10, 10]] ) trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) From d202a0a8c3ad758f851cdc24942a43a96520b3ae Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:16:28 -0400 Subject: [PATCH 08/15] domain_red windows target_space to global_bounds Added windowing function to improve the convergence of optimizers that use domain_reduction. Improved comments and documentation. --- bayes_opt/domain_reduction.py | 129 +++++++++++++++++++++++++--------- tests/test_seq_domain_red.py | 3 +- 2 files changed, 95 insertions(+), 37 deletions(-) diff --git a/bayes_opt/domain_reduction.py b/bayes_opt/domain_reduction.py index 0f5439b8e..fee941c6f 100644 --- a/bayes_opt/domain_reduction.py +++ b/bayes_opt/domain_reduction.py @@ -37,7 +37,10 @@ def __init__( self.minimum_window_value = minimum_window def initialize(self, target_space: TargetSpace) -> None: - """Initialize all of the parameters""" + """Initialize all of the parameters. + """ + + # Set the original bounds self.original_bounds = np.copy(target_space.bounds) self.bounds = [self.original_bounds] @@ -48,6 +51,7 @@ def initialize(self, target_space: TargetSpace) -> None: else: self.minimum_window = [self.minimum_window_value] * len(target_space.bounds) + # Set initial values self.previous_optimal = np.mean(target_space.bounds, axis=1) self.current_optimal = np.mean(target_space.bounds, axis=1) self.r = target_space.bounds[:, 1] - target_space.bounds[:, 0] @@ -73,14 +77,13 @@ def initialize(self, target_space: TargetSpace) -> None: self._window_bounds_compatibility(self.original_bounds) def _update(self, target_space: TargetSpace) -> None: - + """ Updates contraction rate, window size, and window center. + """ # setting the previous self.previous_optimal = self.current_optimal self.previous_d = self.current_d - - self.current_optimal = target_space.params[ - np.argmax(target_space.target) - ] + + self.current_optimal = self._windowed_max(target_space) self.current_d = 2.0 * (self.current_optimal - self.previous_optimal) / self.r @@ -96,46 +99,101 @@ def _update(self, target_space: TargetSpace) -> None: np.abs(self.current_d) * (self.gamma - self.eta) self.r = self.contraction_rate * self.r + + def _windowed_max(self, target_space: TargetSpace) -> np.array: + """Returns the parameters that produce the greatest target value within its bounds. + """ + # extract the components we need from the target space + params = np.copy(target_space.params.T) + target = np.copy(target_space.target) + bounds = np.copy(target_space.bounds) + + # create a mask by checking each params against its bounds + mask = np.zeros_like(target) + for n, row in enumerate(params): + lower_bound = bounds[n, 0] + upper_bound = bounds[n, 1] + mask += (row <= lower_bound).astype(int) + mask += (row >= upper_bound).astype(int) + mask = mask<1 + + # apply the mask + params = params.T[mask] + targets = target[mask] + + best_params = params[np.argmax(targets)] + + return best_params def _trim(self, new_bounds: np.array, global_bounds: np.array) -> np.array: - """ Tests new_bounds to enforce global_bounds and minimum_window.""" - # sort the order of the bounds for each parameter + """ + Adjust the new_bounds and verify that they adhere to global_bounds and minimum_window. + + Parameters: + ----------- + new_bounds : np.array + The proposed new_bounds that (may) need adjustment. + + global_bounds : np.array + The maximum allowable bounds for each parameter. + + Returns: + -------- + new_bounds : np.array + The adjusted bounds after enforcing constraints. + """ + + #sort bounds new_bounds = np.sort(new_bounds) - # check if the new bounds exceed the global bounds + # Validate each parameter's bounds against the global_bounds for i, pbounds in enumerate(new_bounds): - # check if lower bound exceeds range + # If the lower bound of the parameter is outside the global bounds, reset the lower bound if (pbounds[0] < global_bounds[i, 0] or pbounds[0] > global_bounds[i, 1]): pbounds[0] = global_bounds[i, 0] - warn("""Domain reduction warning: - A parameter's lower bound has exceeded its global boundaries. - The offensive boundary has been reset, but the optimizer may not converge.""") - - # check if upper bound exceeds range + warn("""Domain Reduction Warning: + A parameter's lower bound has exceeded its global limit. + The offensive boundary has been reset, but be cautious of optimizer convergence.""") + + # If the upper bound bound of the parameter is outside the global bounds, reset the lower bound if (pbounds[1] > global_bounds[i, 1] or pbounds[1] < global_bounds[i, 0]): pbounds[1] = global_bounds[i, 1] warn("""Domain reduction warning: - A parameter's upper bound has exceeded its global boundaries. - The offensive boundary has been reset, but the optimizer may not converge.""") - - for i, entry in enumerate(new_bounds): - window_width = abs(entry[0] - entry[1]) - if window_width < self.minimum_window[i]: - dw = (self.minimum_window[i] - window_width) / 2.0 - left_expansion_space = abs(global_bounds[i, 0] - entry[0]) # should be non-positive - right_expansion_space = abs(global_bounds[i, 1] - entry[1]) # should be non-negative - # conservative - dw_l = min(dw, left_expansion_space) - dw_r = min(dw, right_expansion_space) - # this crawls towards the edge - ddw_r = dw_r + max(dw - dw_l, 0) - ddw_l = dw_l + max(dw - dw_r, 0) - new_bounds[i, 0] -= ddw_l - new_bounds[i, 1] += ddw_r + A parameter's lower bound has exceeded its global limit. + The offensive boundary has been reset, but be cautious of optimizer convergence.""") + + # Adjust new_bounds to ensure they respect the minimum window width for each parameter + for i, pbounds in enumerate(new_bounds): + current_window_width = abs(pbounds[0] - pbounds[1]) + + # If the window width is less than the minimum allowable width, adjust it + # Note that minimum_window < width of the global bounds one side always has more space than required + if current_window_width < self.minimum_window[i]: + width_deficit = (self.minimum_window[i] - current_window_width) / 2.0 + available_left_space = abs(global_bounds[i, 0] - pbounds[0]) + available_right_space = abs(global_bounds[i, 1] - pbounds[1]) + + # determine how much to expand on the left and right + expand_left = min(width_deficit, available_left_space) + expand_right = min(width_deficit, available_right_space) + + # calculate the deficit on each side + expand_left_deficit = width_deficit - expand_left + expand_right_deficit = width_deficit - expand_right + + # shift the deficit to the side with more space + adjust_left = expand_left + max(expand_right_deficit, 0) + adjust_right = expand_right + max(expand_left_deficit, 0) + + # adjust the bounds + pbounds[0] -= adjust_left + pbounds[1] += adjust_right + return new_bounds def _window_bounds_compatibility(self, global_bounds: np.array) -> bool: - """Checks if global bounds are compatible with the minimum window sizes.""" + """Checks if global bounds are compatible with the minimum window sizes. + """ for i, entry in enumerate(global_bounds): global_window_width = abs(entry[1] - entry[0]) if global_window_width < self.minimum_window[i]: @@ -146,7 +204,8 @@ def _create_bounds(self, parameters: dict, bounds: np.array) -> dict: return {param: bounds[i, :] for i, param in enumerate(parameters)} def transform(self, target_space: TargetSpace) -> dict: - + """Reduces the bounds of the target space. + """ self._update(target_space) new_bounds = np.array( @@ -156,6 +215,6 @@ def transform(self, target_space: TargetSpace) -> dict: ] ).T - self._trim(new_bounds, self.original_bounds) + new_bounds = self._trim(new_bounds, self.original_bounds) self.bounds.append(new_bounds) return self._create_bounds(target_space.keys, new_bounds) diff --git a/tests/test_seq_domain_red.py b/tests/test_seq_domain_red.py index 867ca31b9..48db49edf 100644 --- a/tests/test_seq_domain_red.py +++ b/tests/test_seq_domain_red.py @@ -145,7 +145,7 @@ def test_exceeded_bounds(): bounds_transformer=bounds_transformer ) -def test_trim_both_new_bounds_beyond_gloabal_bounds(): +def test_trim_both_new_bounds_beyond_global_bounds(): """Test if the global bounds are respected when both new bounds for a given parameter are beyond the global bounds.""" @@ -166,7 +166,6 @@ def verify_bounds_in_range(new_bounds, global_bounds): test = False return test - # test if the sorting of the bounds is correct new_bounds = np.array( [[5, -5], [-10, 10]] ) trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) From 70b7b8f7bd20201356d55e046dc507ed8a1e670e Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Sun, 10 Dec 2023 23:59:18 -0500 Subject: [PATCH 09/15] target_space.max respects bounds; SDRT warnings --- bayes_opt/domain_reduction.py | 55 +++++++++++++---------------------- bayes_opt/target_space.py | 54 ++++++++++++++++++++-------------- tests/test_seq_domain_red.py | 13 +++++++-- tests/test_target_space.py | 19 +++++++++--- 4 files changed, 77 insertions(+), 64 deletions(-) diff --git a/bayes_opt/domain_reduction.py b/bayes_opt/domain_reduction.py index fee941c6f..aab1fac18 100644 --- a/bayes_opt/domain_reduction.py +++ b/bayes_opt/domain_reduction.py @@ -83,7 +83,7 @@ def _update(self, target_space: TargetSpace) -> None: self.previous_optimal = self.current_optimal self.previous_d = self.current_d - self.current_optimal = self._windowed_max(target_space) + self.current_optimal = target_space.params_to_array(target_space.max()['params']) self.current_d = 2.0 * (self.current_optimal - self.previous_optimal) / self.r @@ -99,31 +99,6 @@ def _update(self, target_space: TargetSpace) -> None: np.abs(self.current_d) * (self.gamma - self.eta) self.r = self.contraction_rate * self.r - - def _windowed_max(self, target_space: TargetSpace) -> np.array: - """Returns the parameters that produce the greatest target value within its bounds. - """ - # extract the components we need from the target space - params = np.copy(target_space.params.T) - target = np.copy(target_space.target) - bounds = np.copy(target_space.bounds) - - # create a mask by checking each params against its bounds - mask = np.zeros_like(target) - for n, row in enumerate(params): - lower_bound = bounds[n, 0] - upper_bound = bounds[n, 1] - mask += (row <= lower_bound).astype(int) - mask += (row >= upper_bound).astype(int) - mask = mask<1 - - # apply the mask - params = params.T[mask] - targets = target[mask] - - best_params = params[np.argmax(targets)] - - return best_params def _trim(self, new_bounds: np.array, global_bounds: np.array) -> np.array: """ @@ -148,26 +123,36 @@ def _trim(self, new_bounds: np.array, global_bounds: np.array) -> np.array: # Validate each parameter's bounds against the global_bounds for i, pbounds in enumerate(new_bounds): - # If the lower bound of the parameter is outside the global bounds, reset the lower bound - if (pbounds[0] < global_bounds[i, 0] or pbounds[0] > global_bounds[i, 1]): + # If the one of the bounds is outside the global bounds, reset the bound to the global bound + # This is expected to happen when the window is near the global bounds, no warning is issued + if (pbounds[0] < global_bounds[i, 0]): + pbounds[0] = global_bounds[i, 0] + + if (pbounds[1] > global_bounds[i, 1]): + pbounds[1] = global_bounds[i, 1] + + # If a lower bound is greater than the associated global upper bound, reset it to the global lower bound + if (pbounds[0] > global_bounds[i, 1]): pbounds[0] = global_bounds[i, 0] warn("""Domain Reduction Warning: - A parameter's lower bound has exceeded its global limit. - The offensive boundary has been reset, but be cautious of optimizer convergence.""") + A parameter's lower bound is greater than the global upper bound. + The offensive boundary has been reset. + Be cautious of subsequent reductions.""") - # If the upper bound bound of the parameter is outside the global bounds, reset the lower bound - if (pbounds[1] > global_bounds[i, 1] or pbounds[1] < global_bounds[i, 0]): + # If an upper bound is less than the associated global lower bound, reset it to the global upper bound + if (pbounds[1] < global_bounds[i, 0]): pbounds[1] = global_bounds[i, 1] warn("""Domain reduction warning: - A parameter's lower bound has exceeded its global limit. - The offensive boundary has been reset, but be cautious of optimizer convergence.""") + A parameter's upper bound is less than the global lower bound. + The offensive boundary has been reset. + Be cautious of subsequent reductions.""") # Adjust new_bounds to ensure they respect the minimum window width for each parameter for i, pbounds in enumerate(new_bounds): current_window_width = abs(pbounds[0] - pbounds[1]) # If the window width is less than the minimum allowable width, adjust it - # Note that minimum_window < width of the global bounds one side always has more space than required + # Note that when minimum_window < width of the global bounds one side always has more space than required if current_window_width < self.minimum_window[i]: width_deficit = (self.minimum_window[i] - current_window_width) / 2.0 available_left_space = abs(global_bounds[i, 0] - pbounds[0]) diff --git a/bayes_opt/target_space.py b/bayes_opt/target_space.py index e90cfbcf0..86d421624 100644 --- a/bayes_opt/target_space.py +++ b/bayes_opt/target_space.py @@ -116,6 +116,22 @@ def constraint_values(self): if self._constraint is not None: return self._constraint_values + @property + def mask(self): + mask = np.ones_like(self.target, dtype=bool) + + # mask points that don't satisfy the constraint + if self._constraint is not None: + mask &= self._constraint.allowed(self._constraint_values) + + # mask points that are outside the bounds + if self._bounds is not None: + within_bounds = np.all((self._bounds[:, 0] <= self._params) & + (self._params <= self._bounds[:, 1]), axis=1) + mask &= within_bounds + + return mask + def params_to_array(self, params): try: assert set(params) == set(self.keys) @@ -180,7 +196,7 @@ def register(self, params, target, constraint_value=None): 0 >>> x = np.array([0, 0]) >>> y = 1 - >>> space.add_observation(x, y) + >>> space.register(x, y) >>> len(space) 1 """ @@ -263,6 +279,14 @@ def random_sample(self): data.T[col] = self.random_state.uniform(lower, upper, size=1) return data.ravel() + def _pbound_mask(self): + """ + Returns a 1D boolean mask of points that are within the bounds of the space. + """ + + return np.all((self._bounds[:, 0] <= self._params) & + (self._params <= self._bounds[:, 1]), axis=1) + def _target_max(self): """Get maximum target value found. @@ -271,14 +295,10 @@ def _target_max(self): if len(self.target) == 0: return None - if self._constraint is None: - return self.target.max() - - allowed = self._constraint.allowed(self._constraint_values) - if allowed.any(): - return self.target[allowed].max() + if len(self.target[self.mask]) == 0: + return None - return None + return self.target[self.mask].max() def max(self): """Get maximum target value found and corresponding parameters. @@ -286,24 +306,13 @@ def max(self): If there is a constraint present, the maximum value that fulfills the constraint is returned.""" target_max = self._target_max() - if target_max is None: return None - if self._constraint is not None: - allowed = self._constraint.allowed(self._constraint_values) - - target = self.target[allowed] - params = self.params[allowed] - constraint_values = self.constraint_values[allowed] - else: - target = self.target - params = self.params - constraint_values = self.constraint_values + target = self.target[self.mask] + params = self.params[self.mask] + target_max_idx = np.argmax(target) - target_max_idx = np.where(target == target_max)[0][0] - - res = { 'target': target_max, 'params': dict( @@ -312,6 +321,7 @@ def max(self): } if self._constraint is not None: + constraint_values = self.constraint_values[self.mask] res['constraint'] = constraint_values[target_max_idx] return res diff --git a/tests/test_seq_domain_red.py b/tests/test_seq_domain_red.py index 48db49edf..0b6088fcc 100644 --- a/tests/test_seq_domain_red.py +++ b/tests/test_seq_domain_red.py @@ -145,7 +145,7 @@ def test_exceeded_bounds(): bounds_transformer=bounds_transformer ) -def test_trim_both_new_bounds_beyond_global_bounds(): +def test_trim_when_both_new_bounds_exceed_global_bounds(): """Test if the global bounds are respected when both new bounds for a given parameter are beyond the global bounds.""" @@ -171,13 +171,20 @@ def verify_bounds_in_range(new_bounds, global_bounds): trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) assert (trimmed_bounds == np.array( [[-5, 5], [-10, 10]] )).all() - # test if both bounds for one parameter are beyond the global bounds + # test if both (upper/lower) bounds for a parameter exceed the global bounds new_bounds = np.array( [[-50, -20], [-10, 10]] ) trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) assert verify_bounds_in_range(trimmed_bounds, global_bounds) - # test if both bounds for one parameter are beyond the global bounds + # test if both (upper/lower) bounds for a parameter exceed the global bounds # while they are out of order new_bounds = np.array( [[-20, -50], [-10, 10]] ) trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) assert verify_bounds_in_range(trimmed_bounds, global_bounds) + +if __name__ == '__main__': + r""" + CommandLine: + python tests/test_seq_domain_red.py + """ + pytest.main([__file__]) \ No newline at end of file diff --git a/tests/test_target_space.py b/tests/test_target_space.py index c554dea0e..1070a70bf 100644 --- a/tests/test_target_space.py +++ b/tests/test_target_space.py @@ -166,10 +166,10 @@ def test_random_sample(): def test_y_max(): space = TargetSpace(target_func, PBOUNDS) assert space._target_max() == None - space.probe(params={"p1": 1, "p2": 2}) - space.probe(params={"p1": 5, "p2": 1}) + space.probe(params={"p1": 1, "p2": 7}) + space.probe(params={"p1": 0.5, "p2": 1}) space.probe(params={"p1": 0, "p2": 1}) - assert space._target_max() == 6 + assert space._target_max() == 8 def test_y_max_with_constraint(): constraint = ConstraintModel(lambda p1, p2: p1-p2, -2, 2) @@ -180,11 +180,20 @@ def test_y_max_with_constraint(): space.probe(params={"p1": 0, "p2": 1}) # Feasible assert space._target_max() == 3 +def test_y_max_within_pbounds(): + space = TargetSpace(target_func, PBOUNDS) + assert space._target_max() == None + space.probe(params={"p1": 1, "p2": 2}) + space.probe(params={"p1": 5, "p2": 1}) + space.probe(params={"p1": 0, "p2": 1}) + assert space._target_max() == 3 + def test_max(): + PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} space = TargetSpace(target_func, PBOUNDS) - + assert space.max() == None space.probe(params={"p1": 1, "p2": 2}) space.probe(params={"p1": 5, "p2": 4}) @@ -193,6 +202,7 @@ def test_max(): assert space.max() == {"params": {"p1": 5, "p2": 4}, "target": 9} def test_max_with_constraint(): + PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} constraint = ConstraintModel(lambda p1, p2: p1-p2, -2, 2) space = TargetSpace(target_func, PBOUNDS, constraint=constraint) @@ -204,6 +214,7 @@ def test_max_with_constraint(): assert space.max() == {"params": {"p1": 2, "p2": 3}, "target": 5, "constraint": -1} def test_max_with_constraint_identical_target_value(): + PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} constraint = ConstraintModel(lambda p1, p2: p1-p2, -2, 2) space = TargetSpace(target_func, PBOUNDS, constraint=constraint) From ce83a43278bf135791bf962fb8506380bf966f6f Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:30:09 -0500 Subject: [PATCH 10/15] Remove unused function. This function was used to prototype a solution. It should not have been pushed and can be removed. --- bayes_opt/target_space.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/bayes_opt/target_space.py b/bayes_opt/target_space.py index 86d421624..d2112fcc1 100644 --- a/bayes_opt/target_space.py +++ b/bayes_opt/target_space.py @@ -279,14 +279,6 @@ def random_sample(self): data.T[col] = self.random_state.uniform(lower, upper, size=1) return data.ravel() - def _pbound_mask(self): - """ - Returns a 1D boolean mask of points that are within the bounds of the space. - """ - - return np.all((self._bounds[:, 0] <= self._params) & - (self._params <= self._bounds[:, 1]), axis=1) - def _target_max(self): """Get maximum target value found. From 93a46f8f13cdcc9c47b30c48c8ac3c9216c4638c Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:35:30 -0500 Subject: [PATCH 11/15] Updated target_space.py docstrings --- bayes_opt/target_space.py | 74 +++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/bayes_opt/target_space.py b/bayes_opt/target_space.py index d2112fcc1..6f1665444 100644 --- a/bayes_opt/target_space.py +++ b/bayes_opt/target_space.py @@ -19,9 +19,11 @@ class TargetSpace(object): >>> return p1 + p2 >>> pbounds = {'p1': (0, 1), 'p2': (1, 100)} >>> space = TargetSpace(target_func, pbounds, random_state=0) - >>> x = space.random_points(1)[0] - >>> y = space.register_point(x) - >>> assert self.max_point()['max_val'] == y + >>> x = np.array([4 , 5]) + >>> y = target_func(x) + >>> space.register(x, y) + >>> assert self.max()['target'] == 9 + >>> assert self.max()['params'] == {'p1': 1.0, 'p2': 2.0} """ def __init__(self, target_func, pbounds, constraint=None, random_state=None, @@ -118,6 +120,7 @@ def constraint_values(self): @property def mask(self): + '''Returns a boolean array of the points that satisfy the constraint and boundary conditions''' mask = np.ones_like(self.target, dtype=bool) # mask points that don't satisfy the constraint @@ -190,8 +193,9 @@ def register(self, params, target, constraint_value=None): Example ------- + >>> target_func = lambda p1, p2: p1 + p2 >>> pbounds = {'p1': (0, 1), 'p2': (1, 100)} - >>> space = TargetSpace(lambda p1, p2: p1 + p2, pbounds) + >>> space = TargetSpace(target_func, pbounds) >>> len(space) 0 >>> x = np.array([0, 0]) @@ -228,8 +232,8 @@ def register(self, params, target, constraint_value=None): def probe(self, params): """ - Evaluates a single point x, to obtain the value y and then records them - as observations. + Evaluates a single point x, to obtain the value y and then registers the + point and its evaluation. Notes ----- @@ -244,6 +248,15 @@ def probe(self, params): ------- y : float target function value. + + Example + ------- + >>> target_func = lambda p1, p2: p1 + p2 + >>> pbounds = {'p1': (0, 1), 'p2': (1, 100)} + >>> space = TargetSpace(target_func, pbounds) + >>> space.probe([1, 5]) + >>> assert self.max()['target'] == 6 + >>> assert self.max()['params'] == {'p1': 1.0, 'p2': 5.0} """ x = self._as_array(params) params = dict(zip(self._keys, x)) @@ -259,19 +272,19 @@ def probe(self, params): def random_sample(self): """ - Creates random points within the bounds of the space. + Creates a random point within the bounds of the space. Returns ---------- data: ndarray - [num x dim] array points with dimensions corresponding to `self._keys` + [1 x dim] array with dimensions corresponding to `self._keys` Example ------- >>> target_func = lambda p1, p2: p1 + p2 >>> pbounds = {'p1': (0, 1), 'p2': (1, 100)} >>> space = TargetSpace(target_func, pbounds, random_state=0) - >>> space.random_points(1) + >>> space.random_sample() array([[ 55.33253689, 0.54488318]]) """ data = np.empty((1, self.dim)) @@ -280,10 +293,17 @@ def random_sample(self): return data.ravel() def _target_max(self): - """Get maximum target value found. + """Get the maximum target value within the current parameter bounds. If there is a constraint present, the maximum value that fulfills the - constraint is returned.""" + constraint within the parameter bounds is returned. + + Returns + ---------- + max: float + The maximum target value. + + """ if len(self.target) == 0: return None @@ -293,10 +313,21 @@ def _target_max(self): return self.target[self.mask].max() def max(self): - """Get maximum target value found and corresponding parameters. - + """Get the maximum target value within the current parameter bounds, + and its associated parameters. + If there is a constraint present, the maximum value that fulfills the - constraint is returned.""" + constraint within the parameter bounds is returned. + + Returns + ---------- + res: dict + A dictionary with the keys 'target' and 'params'. The value of + 'target' is the maximum target value, and the value of 'params' is + a dictionary with the parameter names as keys and the parameter + values as values. + + """ target_max = self._target_max() if target_max is None: return None @@ -320,7 +351,22 @@ def max(self): def res(self): """Get all target values and constraint fulfillment for all parameters. + + Returns + ---------- + res: list + A list of dictionaries with the keys 'target', 'params', and + 'constraint'. The value of 'target' is the target value, the value + of 'params' is a dictionary with the parameter names as keys and the + parameter values as values, and the value of 'constraint' is the + constraint fulfillment. + + Notes + ----- + Does not report if points are within the bounds of the parameter space. + """ + if self._constraint is None: params = [dict(zip(self.keys, p)) for p in self.params] From 11b525512645450277d815b50de83551326fac58 Mon Sep 17 00:00:00 2001 From: Edgar <50716923+perezed00@users.noreply.github.com> Date: Sun, 17 Dec 2023 17:14:33 -0500 Subject: [PATCH 12/15] Update tests/test_target_space.py Co-authored-by: till-m <36440677+till-m@users.noreply.github.com> --- tests/test_target_space.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_target_space.py b/tests/test_target_space.py index 1070a70bf..808af3ee1 100644 --- a/tests/test_target_space.py +++ b/tests/test_target_space.py @@ -189,7 +189,6 @@ def test_y_max_within_pbounds(): assert space._target_max() == 3 - def test_max(): PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} space = TargetSpace(target_func, PBOUNDS) From cb9d776eb1c04a584cb6854877de7ec129fb7140 Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Mon, 8 Jan 2024 19:37:04 -0500 Subject: [PATCH 13/15] Added pbound warnings, updated various tests. --- bayes_opt/domain_reduction.py | 16 ++++++++-------- bayes_opt/target_space.py | 6 ++++++ tests/test_logs_bounds.log | 5 +++++ tests/test_seq_domain_red.py | 6 ++++-- tests/test_target_space.py | 17 ++++++++++++++++- tests/test_util.py | 22 ++++++++++++++++++---- 6 files changed, 57 insertions(+), 15 deletions(-) create mode 100644 tests/test_logs_bounds.log diff --git a/bayes_opt/domain_reduction.py b/bayes_opt/domain_reduction.py index aab1fac18..40e9416f7 100644 --- a/bayes_opt/domain_reduction.py +++ b/bayes_opt/domain_reduction.py @@ -134,18 +134,18 @@ def _trim(self, new_bounds: np.array, global_bounds: np.array) -> np.array: # If a lower bound is greater than the associated global upper bound, reset it to the global lower bound if (pbounds[0] > global_bounds[i, 1]): pbounds[0] = global_bounds[i, 0] - warn("""Domain Reduction Warning: - A parameter's lower bound is greater than the global upper bound. - The offensive boundary has been reset. - Be cautious of subsequent reductions.""") + warn("\nDomain Reduction Warning:\n"+ + "A parameter's lower bound is greater than the global upper bound."+ + "The offensive boundary has been reset."+ + "Be cautious of subsequent reductions.", stacklevel=2) # If an upper bound is less than the associated global lower bound, reset it to the global upper bound if (pbounds[1] < global_bounds[i, 0]): pbounds[1] = global_bounds[i, 1] - warn("""Domain reduction warning: - A parameter's upper bound is less than the global lower bound. - The offensive boundary has been reset. - Be cautious of subsequent reductions.""") + warn("\nDomain Reduction Warning:\n"+ + "A parameter's lower bound is greater than the global upper bound."+ + "The offensive boundary has been reset."+ + "Be cautious of subsequent reductions.", stacklevel=2) # Adjust new_bounds to ensure they respect the minimum window width for each parameter for i, pbounds in enumerate(new_bounds): diff --git a/bayes_opt/target_space.py b/bayes_opt/target_space.py index 6f1665444..534932f2e 100644 --- a/bayes_opt/target_space.py +++ b/bayes_opt/target_space.py @@ -1,4 +1,5 @@ import numpy as np +from warnings import warn from .util import ensure_rng, NotUniqueError from .util import Colours @@ -214,6 +215,11 @@ def register(self, params, target, constraint_value=None): raise NotUniqueError(f'Data point {x} is not unique. You can set "allow_duplicate_points=True" to ' f'avoid this error') + # if x is not within the bounds of the parameter space, warn the user + if self._bounds is not None: + if not np.all((self._bounds[:, 0] <= x) & (x <= self._bounds[:, 1])): + warn(f'\nData point {x} is outside the bounds of the parameter space. ', stacklevel=2) + self._params = np.concatenate([self._params, x.reshape(1, -1)]) self._target = np.concatenate([self._target, [target]]) diff --git a/tests/test_logs_bounds.log b/tests/test_logs_bounds.log new file mode 100644 index 000000000..0a7d9ba46 --- /dev/null +++ b/tests/test_logs_bounds.log @@ -0,0 +1,5 @@ +{"datetime": {"delta": 0.0, "datetime": "2018-11-25 08:29:25", "elapsed": 0.0}, "params": {"y": 0, "x": 0}, "target": 0} +{"datetime": {"delta": 0.001301, "datetime": "2018-11-25 08:29:25", "elapsed": 0.001301}, "params": {"y": 1, "x": 1}, "target": 2} +{"datetime": {"delta": 1.075242, "datetime": "2018-11-25 08:29:26", "elapsed": 1.076543}, "params": {"y": 2, "x": 2}, "target": 4} +{"datetime": {"delta": 0.239797, "datetime": "2018-11-25 08:29:26", "elapsed": 1.31634}, "params": {"y": 3, "x": 3}, "target": 6} +{"datetime": {"delta": 0.001301, "datetime": "2018-11-25 08:29:25", "elapsed": 0.001301}, "params": {"y": 1.5, "x": 1.5}, "target": 3} diff --git a/tests/test_seq_domain_red.py b/tests/test_seq_domain_red.py index 0b6088fcc..23890b870 100644 --- a/tests/test_seq_domain_red.py +++ b/tests/test_seq_domain_red.py @@ -173,13 +173,15 @@ def verify_bounds_in_range(new_bounds, global_bounds): # test if both (upper/lower) bounds for a parameter exceed the global bounds new_bounds = np.array( [[-50, -20], [-10, 10]] ) - trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) + with pytest.warns(UserWarning): + trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) assert verify_bounds_in_range(trimmed_bounds, global_bounds) # test if both (upper/lower) bounds for a parameter exceed the global bounds # while they are out of order new_bounds = np.array( [[-20, -50], [-10, 10]] ) - trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) + with pytest.warns(UserWarning): + trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) assert verify_bounds_in_range(trimmed_bounds, global_bounds) if __name__ == '__main__': diff --git a/tests/test_target_space.py b/tests/test_target_space.py index 808af3ee1..24ea371bf 100644 --- a/tests/test_target_space.py +++ b/tests/test_target_space.py @@ -74,6 +74,7 @@ def test_as_array(): def test_register(): + PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} space = TargetSpace(target_func, PBOUNDS) assert len(space) == 0 @@ -96,6 +97,7 @@ def test_register(): def test_register_with_constraint(): + PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} constraint = ConstraintModel(lambda x: x, -2, 2) space = TargetSpace(target_func, PBOUNDS, constraint=constraint) @@ -118,7 +120,16 @@ def test_register_with_constraint(): space.register(params={"p1": 2, "p2": 2}, target=3) +def test_register_point_beyond_bounds(): + PBOUNDS = {'p1': (0, 1), 'p2': (1, 10)} + space = TargetSpace(target_func, PBOUNDS) + + with pytest.warns(UserWarning): + space.register(params={"p1": 0.5, "p2": 20}, target=2.5) + + def test_probe(): + PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} space = TargetSpace(target_func, PBOUNDS, allow_duplicate_points=True) assert len(space) == 0 @@ -172,6 +183,7 @@ def test_y_max(): assert space._target_max() == 8 def test_y_max_with_constraint(): + PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} constraint = ConstraintModel(lambda p1, p2: p1-p2, -2, 2) space = TargetSpace(target_func, PBOUNDS, constraint) assert space._target_max() == None @@ -181,11 +193,13 @@ def test_y_max_with_constraint(): assert space._target_max() == 3 def test_y_max_within_pbounds(): + PBOUNDS = {'p1': (0, 2), 'p2': (1, 100)} space = TargetSpace(target_func, PBOUNDS) assert space._target_max() == None space.probe(params={"p1": 1, "p2": 2}) - space.probe(params={"p1": 5, "p2": 1}) space.probe(params={"p1": 0, "p2": 1}) + with pytest.warns(UserWarning): + space.probe(params={"p1": 5, "p2": 1}) assert space._target_max() == 3 @@ -226,6 +240,7 @@ def test_max_with_constraint_identical_target_value(): assert space.max() == {"params": {"p1": 2, "p2": 3}, "target": 5, "constraint": -1} def test_res(): + PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} space = TargetSpace(target_func, PBOUNDS) assert space.res() == [] diff --git a/tests/test_util.py b/tests/test_util.py index 16ec28fd6..5414b3566 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -132,7 +132,7 @@ def f(x, y): optimizer = BayesianOptimization( f=f, - pbounds={"x": (-2, 2), "y": (-2, 2)} + pbounds={"x": (-200, 200), "y": (-200, 200)} ) assert len(optimizer.space) == 0 @@ -150,6 +150,21 @@ def f(x, y): load_logs(other_optimizer, [str(test_dir / "test_logs.log")]) +def test_logs_bounds(): + def f(x, y): + return x + y + + optimizer = BayesianOptimization( + f=f, + pbounds={"x": (-2, 2), "y": (-2, 2)} + ) + + with pytest.warns(UserWarning): + load_logs(optimizer, [str(test_dir / "test_logs_bounds.log")]) + + assert len(optimizer.space) == 5 + + def test_logs_constraint(): def f(x, y): @@ -162,7 +177,7 @@ def c(x, y): optimizer = BayesianOptimization( f=f, - pbounds={"x": (-2, 2), "y": (-2, 2)}, + pbounds={"x": (-200, 200), "y": (-200, 200)}, constraint=constraint ) @@ -201,5 +216,4 @@ def test_colours(): CommandLine: python tests/test_target_space.py """ - import pytest - pytest.main([__file__]) + pytest.main([__file__]) \ No newline at end of file From 5321fdeb491ad92febf5ddcc62e0c4c9e3053631 Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Mon, 8 Jan 2024 19:40:58 -0500 Subject: [PATCH 14/15] updated line spacing for consistency and style --- tests/test_seq_domain_red.py | 3 +++ tests/test_target_space.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/tests/test_seq_domain_red.py b/tests/test_seq_domain_red.py index 23890b870..37ec9ddf5 100644 --- a/tests/test_seq_domain_red.py +++ b/tests/test_seq_domain_red.py @@ -68,6 +68,7 @@ def reset(self): assert not (standard_optimizer._space.bounds == mutated_optimizer._space.bounds).any() + def test_minimum_window_is_kept(): bounds_transformer = SequentialDomainReductionTransformer(minimum_window=1.0) pbounds = {'x': (-0.5, 0.5), 'y': (-1.0, 0.0)} @@ -106,6 +107,7 @@ def test_minimum_window_array_is_kept(): window_widths = np.diff(bounds_transformer.bounds) assert np.all(np.isclose(np.squeeze(np.min(window_widths, axis=0)), window_ranges)) + def test_trimming_bounds(): """Test if the bounds are trimmed correctly within the bounds""" def dummy_function(x1, x2, x3, x4, x5): @@ -145,6 +147,7 @@ def test_exceeded_bounds(): bounds_transformer=bounds_transformer ) + def test_trim_when_both_new_bounds_exceed_global_bounds(): """Test if the global bounds are respected when both new bounds for a given parameter are beyond the global bounds.""" diff --git a/tests/test_target_space.py b/tests/test_target_space.py index 24ea371bf..494b42495 100644 --- a/tests/test_target_space.py +++ b/tests/test_target_space.py @@ -182,6 +182,7 @@ def test_y_max(): space.probe(params={"p1": 0, "p2": 1}) assert space._target_max() == 8 + def test_y_max_with_constraint(): PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} constraint = ConstraintModel(lambda p1, p2: p1-p2, -2, 2) @@ -192,6 +193,7 @@ def test_y_max_with_constraint(): space.probe(params={"p1": 0, "p2": 1}) # Feasible assert space._target_max() == 3 + def test_y_max_within_pbounds(): PBOUNDS = {'p1': (0, 2), 'p2': (1, 100)} space = TargetSpace(target_func, PBOUNDS) @@ -214,6 +216,7 @@ def test_max(): space.probe(params={"p1": 1, "p2": 6}) assert space.max() == {"params": {"p1": 5, "p2": 4}, "target": 9} + def test_max_with_constraint(): PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} constraint = ConstraintModel(lambda p1, p2: p1-p2, -2, 2) @@ -226,6 +229,7 @@ def test_max_with_constraint(): space.probe(params={"p1": 1, "p2": 6}) # Unfeasible assert space.max() == {"params": {"p1": 2, "p2": 3}, "target": 5, "constraint": -1} + def test_max_with_constraint_identical_target_value(): PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} constraint = ConstraintModel(lambda p1, p2: p1-p2, -2, 2) @@ -239,6 +243,7 @@ def test_max_with_constraint_identical_target_value(): space.probe(params={"p1": 1, "p2": 6}) # Unfeasible assert space.max() == {"params": {"p1": 2, "p2": 3}, "target": 5, "constraint": -1} + def test_res(): PBOUNDS = {'p1': (0, 10), 'p2': (1, 100)} space = TargetSpace(target_func, PBOUNDS) From 6996dd1bb1766944c6994ab42764690c4ba12940 Mon Sep 17 00:00:00 2001 From: perezed00 <50716923+perezed00@users.noreply.github.com> Date: Tue, 9 Jan 2024 15:57:48 -0500 Subject: [PATCH 15/15] added pbound test condition --- tests/test_seq_domain_red.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_seq_domain_red.py b/tests/test_seq_domain_red.py index 37ec9ddf5..f03dc71dc 100644 --- a/tests/test_seq_domain_red.py +++ b/tests/test_seq_domain_red.py @@ -175,7 +175,7 @@ def verify_bounds_in_range(new_bounds, global_bounds): assert (trimmed_bounds == np.array( [[-5, 5], [-10, 10]] )).all() # test if both (upper/lower) bounds for a parameter exceed the global bounds - new_bounds = np.array( [[-50, -20], [-10, 10]] ) + new_bounds = np.array( [[-50, -20], [20, 50]] ) with pytest.warns(UserWarning): trimmed_bounds = bounds_transformer._trim(new_bounds, global_bounds) assert verify_bounds_in_range(trimmed_bounds, global_bounds)