Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add zeta param to control TR eps init #832

Merged
merged 2 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 35 additions & 11 deletions tests/unit/acquisition/test_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -1387,7 +1387,8 @@ def test_trust_region_box_get_dataset_min_outside_search_space() -> None:
npt.assert_array_equal(y_min, tf.constant([np.inf], dtype=tf.float64))


def test_trust_region_box_initialize() -> None:
@pytest.mark.parametrize("zeta", [None, 0.1, 0.7])
def test_trust_region_box_initialize(zeta: Optional[float]) -> None:
"""Initialize sets the box to a random location, and sets the eps and y_min values."""
search_space = Box([0.0, 0.0], [1.0, 1.0])
datasets = {
Expand All @@ -1397,10 +1398,15 @@ def test_trust_region_box_initialize() -> None:
)
}
# Includes a quick test of input_active_dims. The irrelevant input dimension should be ignored.
trb = SingleObjectiveTrustRegionBox(search_space, input_active_dims=[1, 2])
if zeta is not None:
trb = SingleObjectiveTrustRegionBox(search_space, zeta=zeta, input_active_dims=[1, 2])
else:
trb = SingleObjectiveTrustRegionBox(search_space, input_active_dims=[1, 2])
trb.initialize(datasets=datasets)

exp_eps = 0.5 * (search_space.upper - search_space.lower) / 5.0 ** (1.0 / 2.0)
exp_zeta = zeta if zeta is not None else 0.5 # Default value.
exp_eps = exp_zeta * (search_space.upper - search_space.lower)

npt.assert_array_equal(trb.eps, exp_eps)
npt.assert_array_compare(np.less_equal, search_space.lower, trb.location)
npt.assert_array_compare(np.less_equal, trb.location, search_space.upper)
Expand All @@ -1419,7 +1425,7 @@ def test_trust_region_box_requires_initialization() -> None:
tf.constant([[0.7], [0.9]], dtype=tf.float64),
)
}
trb = SingleObjectiveTrustRegionBox(search_space, min_eps=0.5)
trb = SingleObjectiveTrustRegionBox(search_space, min_eps=0.7)
trb.initialize(datasets=datasets)
location = trb.location

Expand All @@ -1446,9 +1452,12 @@ def test_trust_region_box_update_no_initialize() -> None:
)
}
# Includes a quick test of input_active_dims. The irrelevant input dimension should be ignored.
trb = SingleObjectiveTrustRegionBox(search_space, min_eps=0.1, input_active_dims=[0, 2])
trb = SingleObjectiveTrustRegionBox(
search_space, zeta=0.3, min_eps=0.1, input_active_dims=[0, 2]
)
trb.initialize(datasets=datasets)
trb.location = tf.constant([0.5, 0.5], dtype=tf.float64)
trb._update_bounds()
location = trb.location

assert not trb.requires_initialization
Expand Down Expand Up @@ -1641,12 +1650,13 @@ def __init__(
global_search_space: SearchSpace,
beta: float = 0.7,
kappa: float = 1e-4,
zeta: float = 0.5,
min_eps: float = 1e-2,
init_eps: float = 0.07,
):
self._location = fixed_location
self._init_eps_val = init_eps
super().__init__(global_search_space, beta, kappa, min_eps)
super().__init__(global_search_space, beta=beta, kappa=kappa, zeta=zeta, min_eps=min_eps)

@property
def location(self) -> TensorType:
Expand All @@ -1667,7 +1677,11 @@ def test_multi_trust_region_box_inits_regions_that_need_it() -> None:

subspaces = [
TestTrustRegionBox(
tf.constant([0.5 + i * 0.1], dtype=tf.float64), search_space, min_eps=0.3, init_eps=0.4
tf.constant([0.5 + i * 0.1], dtype=tf.float64),
search_space,
zeta=0.1,
min_eps=0.3,
init_eps=0.4,
)
for i in range(3)
]
Expand Down Expand Up @@ -2087,7 +2101,10 @@ def test_trust_region_discrete_get_dataset_min_outside_search_space(
npt.assert_array_equal(y_min, tf.constant([np.inf], dtype=tf.float64))


def test_trust_region_discrete_initialize(discrete_search_space: DiscreteSearchSpace) -> None:
@pytest.mark.parametrize("zeta", [None, 0.1, 0.7])
def test_trust_region_discrete_initialize(
discrete_search_space: DiscreteSearchSpace, zeta: Optional[float]
) -> None:
"""Check initialize sets the region to a random location, and sets the eps and y_min values."""
datasets = {
OBJECTIVE: Dataset( # Points outside the search space should be ignored.
Expand All @@ -2096,10 +2113,17 @@ def test_trust_region_discrete_initialize(discrete_search_space: DiscreteSearchS
)
}
# Includes a quick test of input_active_dims. The irrelevant input dimension should be ignored.
tr = SingleObjectiveTrustRegionDiscrete(discrete_search_space, input_active_dims=[1, 2])
if zeta is not None:
tr = SingleObjectiveTrustRegionDiscrete(
discrete_search_space, zeta=zeta, input_active_dims=[1, 2]
)
else:
tr = SingleObjectiveTrustRegionDiscrete(discrete_search_space, input_active_dims=[1, 2])
tr.initialize(datasets=datasets)

exp_eps = 0.5 * (discrete_search_space.upper - discrete_search_space.lower) / 5.0 ** (1.0 / 2.0)
exp_zeta = zeta if zeta is not None else 0.5 # Default value.
exp_eps = exp_zeta * (discrete_search_space.upper - discrete_search_space.lower)

npt.assert_array_equal(tr.eps, exp_eps)
npt.assert_array_compare(np.less_equal, discrete_search_space.lower, tr.location)
npt.assert_array_compare(np.less_equal, tr.location, discrete_search_space.upper)
Expand All @@ -2119,7 +2143,7 @@ def test_trust_region_discrete_requires_initialization(
tf.constant([[0.7], [0.9]], dtype=tf.float64),
)
}
tr = SingleObjectiveTrustRegionDiscrete(discrete_search_space, min_eps=3.0)
tr = SingleObjectiveTrustRegionDiscrete(discrete_search_space, min_eps=4.0)
tr.initialize(datasets=datasets)
tr._location_ix = tf.constant([], dtype=tf.int32)
location = tr.location
Expand Down
16 changes: 10 additions & 6 deletions trieste/acquisition/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,7 @@ def __init__(
global_search_space: SearchSpace,
beta: float = 0.7,
kappa: float = 1e-4,
zeta: float = 0.5,
min_eps: float = 1e-2,
region_index: Optional[int] = None,
input_active_dims: Optional[Union[slice, Sequence[int]]] = None,
Expand All @@ -1544,6 +1545,8 @@ def __init__(
:param beta: The inverse of the trust region contraction factor.
:param kappa: Scales the threshold for the minimal improvement required for a step to be
considered a success.
:param zeta: The initial size of the trust region is ``zeta`` times the size of the global
search space.
:param min_eps: The minimal size of the search space. If the size of the search space is
smaller than this, the search space is reinitialized.
:param region_index: The index of the region in a multi-region search space. This is used to
Expand All @@ -1555,6 +1558,7 @@ def __init__(
super().__init__(global_search_space, region_index, input_active_dims)
self._beta = beta
self._kappa = kappa
self._zeta = zeta
self._min_eps = min_eps

self._step_is_success = False
Expand All @@ -1574,9 +1578,7 @@ def requires_initialization(self) -> bool:
return not self._initialized or tf.reduce_any(self.eps < self._min_eps)

def _init_eps(self) -> None:
global_lower = self.global_search_space.lower
global_upper = self.global_search_space.upper
self.eps = 0.5 * (global_upper - global_lower) / (5.0 ** (1.0 / global_lower.shape[-1]))
self.eps = self._zeta * (self.global_search_space.upper - self.global_search_space.lower)

def _update_bounds(self) -> None:
self._lower = tf.reduce_max(
Expand Down Expand Up @@ -2109,6 +2111,7 @@ def __init__(
global_search_space: DiscreteSearchSpace,
beta: float = 0.7,
kappa: float = 1e-4,
zeta: float = 0.5,
min_eps: float = 1e-2,
region_index: Optional[int] = None,
input_active_dims: Optional[Union[slice, Sequence[int]]] = None,
Expand All @@ -2121,6 +2124,8 @@ def __init__(
:param beta: The inverse of the trust region contraction factor.
:param kappa: Scales the threshold for the minimal improvement required for a step to be
considered a success.
:param zeta: The initial size of the trust region is ``zeta`` times the size of the global
search space.
:param min_eps: The minimal size of the search space. If the size of the search space is
smaller than this, the search space is reinitialized.
:param region_index: The index of the region in a multi-region search space. This is used to
Expand All @@ -2132,6 +2137,7 @@ def __init__(
super().__init__(global_search_space, region_index, input_active_dims)
self._beta = beta
self._kappa = kappa
self._zeta = zeta
self._min_eps = min_eps
self._step_is_success = False
self._init_location()
Expand Down Expand Up @@ -2168,9 +2174,7 @@ def _init_location(self) -> None:
)[0]

def _init_eps(self) -> None:
global_lower = self.global_search_space.lower
global_upper = self.global_search_space.upper
self.eps = 0.5 * (global_upper - global_lower) / (5.0 ** (1.0 / global_lower.shape[-1]))
self.eps = self._zeta * (self.global_search_space.upper - self.global_search_space.lower)

def _compute_global_distances(self) -> None:
# Pairwise distances along each axis in the global search space.
Expand Down
Loading