8000 Use QuasiRandomDesigner in EagleStrategy. by copybara-service[bot] · Pull Request #943 · google/vizier · GitHub
[go: up one dir, main page]

Skip to content
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
41 changes: 29 additions & 12 deletions vizier/_src/algorithms/designers/eagle_strategy/eagle_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@
import numpy as np
from vizier import algorithms as vza
from vizier import pyvizier as vz
from vizier._src.algorithms.core import abstractions
from vizier._src.algorithms.designers import quasi_random
from vizier._src.algorithms.designers.eagle_strategy import eagle_strategy_utils
from vizier._src.algorithms.designers.eagle_strategy import serialization
from vizier._src.algorithms.random import random_sample
from vizier.interfaces import serializable
from vizier.pyvizier import converters

Expand All @@ -101,13 +102,16 @@ def __init__(
*,
config: Optional[FireflyAlgorithmConfig] = None,
seed: Optional[int] = None,
initial_designer_factory: Optional[abstractions.DesignerFactory] = None,
):
"""Initializes the Eagle Strategy desiger.
"""Initializes the Eagle Strategy designer.

Args:
problem_statement: A problem description including the search space.
config: The Firefly algorithm hyperparameters.
seed: A seed to deterministically generate samples from random variables.
initial_designer_factory: A partially serializable designer factory to
generate the initial suggestions.

Raises:
Exception: if the problem statement includes condional search space,
Expand Down Expand Up @@ -144,6 +148,10 @@ def __init__(
self._firefly_pool = FireflyPool(
utils=self._utils, capacity=self._utils.compute_pool_capacity())

if initial_designer_factory is None:
initial_designer_factory = quasi_random.QuasiRandomDesigner.from_problem
self._initial_designer = initial_designer_factory(self._problem, seed=seed)

logging.info(
(
'Eagle Strategy designer initialized. Pool capacity: %s. '
Expand All @@ -166,6 +174,9 @@ def dump(self) -> vz.Metadata:
serialization.partially_serialize_firefly_pool(self._firefly_pool))
metadata.ns('eagle')['serialization_version'] = 'v1'
metadata.ns('eagle')['dump_timestamp'] = str(time.time())
metadata.ns('eagle').ns('random_designer').attach(
self._initial_designer.dump()
)
logging.info('Dump metadata:\n%s', metadata.ns('eagle'))
return metadata

Expand All @@ -178,6 +189,7 @@ def load(self, metadata: vz.Metadata) -> None:

1. Set EagleStrategy, EagleStrategyUtils with the recovered random generator
2. Recover the FireflyPool and populate it with the Fireflies.
3. Recover the random designer state.

Args:
metadata: Metadata
Expand All @@ -187,14 +199,14 @@ def load(self, metadata: vz.Metadata) -> None:
logging.info('Eagle designer was called for the first time. No state was'
' recovered.')
else:
logging.info('Load metadata:\n%s', metadata.ns('eagle'))
try:
logging.info('Load metadata:\n%s', metadata.ns('eagle'))
self._rng = serialization.restore_rng(metadata.ns('eagle')['rng'])
except Exception as e:
raise serializable.FatalDecodeError(
"Couldn't load random generator from metadata.") from e
self._utils.rng = self._rng
logging.info('Restored rng:\n%s', self._rng)

try:
firefly_pool = metadata.ns('eagle')['firefly_pool']
self._firefly_pool = serialization.restore_firefly_pool(
Expand All @@ -203,6 +215,17 @@ def load(self, metadata: vz.Metadata) -> None:
except Exception as e:
raise serializable.HarmlessDecodeError(
"Couldn't load firefly pool from metadata.") from e

try:
self._initial_designer = quasi_random.QuasiRandomDesigner(
self._problem.search_space
)
self._initial_designer.load(metadata.ns('eagle').ns('random_designer'))
except Exception as e:
raise serializable.HarmlessDecodeError(
"Couldn't load random designer from metadata."
) from e

logging.info(
('Eagle designer restored state from timestamp %s. Firefly pool'
' now contains %s fireflies.'),
Expand All @@ -224,22 +247,17 @@ def _suggest_one(self) -> vz.TrialSuggestion:
parameters inplace and assign them to the suggested trial.

Returns:
The suggested trial
The parent fly Id
The suggested trial with the parent fly Id in the metadata.
"""
suggested_trial = vz.TrialSuggestion()
if self._firefly_pool.size < self._firefly_pool.capacity:
# Pool is underpopulated. Generate a random trial parameters.
# (b/243518714): Use random policy/designer to generate parameters.
suggested_parameters = random_sample.sample_parameters(
self._rng, self._problem.search_space
)
suggested_parameters = self._initial_designer.suggest()[0].parameters
# Create a new parent fly id and assign it to the trial, this will be
# used during Update to match the trial to its parent fly in the pool.
parent_fly_id = self._firefly_pool.generate_new_fly_id()
logging.info('Pool is underpopulated. Generated random trial parameters.')
else:
# The pool is full. Use a copy of the next fly in line to be moved.
moving_fly = self._firefly_pool.get_next_moving_fly_copy()
self._mutate_fly(moving_fly)
self._perturb_fly(moving_fly)
Expand All @@ -264,7 +282,6 @@ def _mutate_fly(self, moving_fly: Firefly) -> None:
Args:
moving_fly: the fire from the pool to mutate its trial parameters.
"""
# Access the moving fly's trial parameters to be modified.
mutated_parameters = moving_fly.trial.parameters
# Shuffle the ordering, so to apply the attracting and repelling forces
# in random order every time. Creates a deep copy of the pool members.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,22 @@

class EagleStrategyTest(parameterized.TestCase):

def test_dump_and_load(self):
eagle_designer = testing.create_fake_populated_eagle_designer()
partial_serialized_eagle = eagle_designer.dump()
@parameterized.parameters(
testing.create_fake_populated_eagle_designer, # suggest by mutation.
testing.create_fake_empty_eagle_designer, # suggest by initial designer.
)
def test_dump_and_load(self, designer_factory):
eagle_designer = designer_factory()
metadata = eagle_designer.dump()
# Create a new eagle designer and load state
eagle_designer_restored = testing.create_fake_empty_eagle_designer()
eagle_designer_restored.load(partial_serialized_eagle)
# Generate suggestions from the two designers
trial_suggestions = eagle_designer.suggest(count=1)
trial_suggestions_recovered = eagle_designer_restored.suggest(count=1)
# Test if the suggestion from the two designers equal
self.assertEqual(trial_suggestions[0].parameters,
trial_suggestions_recovered[0].parameters)
eagle_designer_restored.load(metadata)
# Generate suggestions from the two designers and test if they're equal.
for _ in range(10):
self.assertEqual(
eagle_designer.suggest()[0].parameters,
eagle_designer_restored.suggest()[0].parameters,
)

def test_load_with_no_state(self):
problem = testing.create_fake_problem_statement()
Expand Down Expand Up @@ -77,7 +81,6 @@ def test_embedding(self):
)
complete_trials = vza.CompletedTrials([trial])
eagle_designer.update(complete_trials, vza.ActiveTrials())
print(eagle_designer._firefly_pool)
self.assertEqual(
eagle_designer._firefly_pool._pool[0].trial.parameters['x'].value, 1.0
)
Expand Down Expand Up @@ -143,48 +146,64 @@ def test_update_capacitated_pool_no_parent_fly_trial_is_better(self):
# Capacitated pool size has 11 fireflies.
eagle_designer = testing.create_fake_populated_eagle_designer(
x_values=[1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1],
obj_values=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
obj_values=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
)
trial = testing.create_fake_trial(
parent_fly_id=98, x_value=1.42, obj_value=100.0)
parent_fly_id=98, x_value=1.42, obj_value=100.0
)
eagle_designer._update_one(trial)
self.assertIs(eagle_designer._firefly_pool._pool[3].trial, trial)

def test_update_capacitated_pool_no_parent_fly_trial_is_not_better(self):
eagle_designer = testing.create_fake_populated_eagle_designer(
x_values=[1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1],
obj_values=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
obj_values=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
)
trial = testing.create_fake_trial(
parent_fly_id=98, x_value=1.42, obj_value=-80.0)
parent_fly_id=98, x_value=1.42, obj_value=-80.0
)
prev_trial = eagle_designer._firefly_pool._pool[3].trial
eagle_designer._update_one(trial)
self.assertIs(eagle_designer._firefly_pool._pool[3].trial, prev_trial)

def test_update_capacitated_pool_with_parent_fly_trial_is_better(self):
eagle_designer = testing.create_fake_populated_eagle_designer(
x_values=[1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1],
obj_values=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
obj_values=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
)
trial = testing.create_fake_trial(
parent_fly_id=2, x_value=3.3, obj_value=80.0)
parent_fly_id=2, x_value=3.3, obj_value=80.0
)
eagle_designer._update_one(trial)
self.assertIs(eagle_designer._firefly_pool._pool[2].trial, trial)

def test_update_capacitated_pool_with_parent_fly_trial_is_not_better(self):
eagle_designer = testing.create_fake_populated_eagle_designer(
x_values=[1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1],
obj_values=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
obj_values=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
)
trial = testing.create_fake_trial(
parent_fly_id=2, x_value=3.3, obj_value=-80.0)
parent_fly_id=2, x_value=3.3, obj_value=-80.0
)
prev_trial = eagle_designer._firefly_pool._pool[2].trial
eagle_designer._update_one(trial)
self.assertIs(eagle_designer._firefly_pool._pool[2].trial, prev_trial)

def test_update_empty_pool(self):
eagle_designer = testing.create_fake_empty_eagle_designer()
trial = testing.create_fake_trial(
parent_fly_id=0, x_value=3.3, obj_value=0.0)
parent_fly_id=0, x_value=3.3, obj_value=0.0
)
eagle_designer._update_one(trial)
self.assertIs(eagle_designer._firefly_pool._pool[0].trial, trial)

def test_seeded_random_samples(self):
problem = testing.create_fake_problem_statement()
suggestions1 = EagleStrategyDesigner(problem, seed=1).suggest(count=3)
suggestions2 = EagleStrategyDesigner(problem, seed=1).suggest(count=3)
for suggestions1, suggest2 in zip(suggestions1, suggestions2):
self.assertEqual(suggestions1.parameters, suggest2.parameters)

@parameterized.parameters(1, 3, 5)
def test_suggest_update(self, batch_size):
problem = vz.ProblemStatement()
Expand Down
0