in syne_tune/optimizer/schedulers/searchers/bayesopt/models/gpiss_model.py [0:0]
def _draw_fantasy_values(
self, state: TuningJobState) -> TuningJobState:
"""
Note: Fantasized target values are not de-normalized, because they
are used internally only (see `prepare_data` with
`do_fantasizing=True`).
:param state: State with pending evaluations without fantasies
:return: Copy of `state`, where `pending_evaluations` contains
fantasized target values
"""
assert self.num_fantasy_samples > 0
# Fantasies are drawn in sequential chunks, one trial with pending
# evaluations at a time.
data_nopending, data_pending = prepare_data_with_pending(
state=state,
configspace_ext=self._configspace_ext,
active_metric=self.active_metric,
normalize_targets=self.normalize_targets)
if not data_nopending['configs']:
# It can happen that all trials with observed data also have
# pending evaluations. This is possible only at the very start,
# as long as no trial has been stopped or paused.
# In this case, we find the trial with the largest number of
# observed targets and remove its pending evaluations, so
# `data_nopending` gets one entry. It is not possible to compute
# a posterior state without any data, so handling this case
# correctly would be very tedious).
assert data_pending['configs'], \
"State is empty, cannot do posterior inference:\n" +\
str(state)
names = ('configs', 'targets', 'trial_ids')
elem = {k: data_pending[k].pop(0) for k in names}
for k, v in elem.items():
data_nopending[k] = [v]
k = 'features'
all_features = data_pending[k]
data_nopending[k] = all_features[0].reshape((1, -1))
data_pending[k] = all_features[1:, :]
logger.info(
"All trials currently have pending evaluations. In order to "
"sample fantasy targets, I'll remove pending evaluations "
f"from trial_id {elem['trial_ids']} (which has "
f"{elem['targets'].size} observations)")
# Start with posterior state, conditioned on data from trials without
# pending evaluations
self._gpmodel.recompute_states(data_nopending)
poster_state_nopending = self._gpmodel.states[0]
# Loop over trials with pending evaluations: For each trial, we sample
# fantasy targets given observed ones, then update `poster_state` by
# conditioning on both. This ensures we obtain a joint sample (the
# ordering of trials does not matter). For the application here, we
# do not need the final `poster_state`.
all_fantasy_targets = []
for sample_it in range(self.num_fantasy_samples):
fantasy_targets, _ = poster_state_nopending.sample_and_update_for_pending(
data_pending, sample_all_nonobserved=False,
random_state=self._gpmodel.random_state)
for pos, fantasies in enumerate(fantasy_targets):
if sample_it == 0:
all_fantasy_targets.append([fantasies])
else:
all_fantasy_targets[pos].append(fantasies)
# Convert into `FantasizedPendingEvaluation`
r_min = self._configspace_ext.resource_attr_range[0]
pending_evaluations_with_fantasies = []
for trial_id, targets, fantasies in zip(
data_pending['trial_ids'], data_pending['targets'],
all_fantasy_targets):
n_observed = targets.size
n_pending = fantasies[0].size
start = r_min + n_observed
resources = list(range(start, start + n_pending))
fantasy_matrix = np.hstack(v.reshape((-1, 1)) for v in fantasies)
assert fantasy_matrix.shape == (n_pending, self.num_fantasy_samples)
for resource, fantasy in zip(resources, fantasy_matrix):
pending_evaluations_with_fantasies.append(
FantasizedPendingEvaluation(
trial_id=trial_id,
fantasies={self.active_metric: fantasy},
resource=resource))
# Return new state, with `pending_evaluations` replaced
return TuningJobState(
hp_ranges=state.hp_ranges,
config_for_trial=state.config_for_trial,
trials_evaluations=state.trials_evaluations,
failed_trials=state.failed_trials,
pending_evaluations=pending_evaluations_with_fantasies)