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

Follow the Optuna convention #189

Merged
merged 4 commits into from
Dec 2, 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
26 changes: 12 additions & 14 deletions package/samplers/cmamae/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ quality diversity algorithms using pyribs.

- CmaMaeSampler

Please take a look at:

- [GridArchive](https://docs.pyribs.org/en/stable/api/ribs.archives.GridArchive.html), and
- [EvolutionStrategyEmitter](https://docs.pyribs.org/en/stable/api/ribs.emitters.EvolutionStrategyEmitter.html)
for the details of each argument.

## Installation

```shell
Expand All @@ -42,22 +48,24 @@ $ pip install ribs
```python
import optuna
import optunahub
from optuna.study import StudyDirection

module = optunahub.load_module("samplers/cmamae")
CmaMaeSampler = module.CmaMaeSampler


def objective(trial: optuna.trial.Trial) -> tuple[float, float, float]:
def objective(trial: optuna.trial.Trial) -> float:
"""Returns an objective followed by two measures."""
x = trial.suggest_float("x", -10, 10)
y = trial.suggest_float("y", -10, 10)
return x**2 + y**2, x, y
trial.set_user_attr("m0", 2 * x)
trial.set_user_attr("m1", x + y)
return x**2 + y**2


if __name__ == "__main__":
sampler = CmaMaeSampler(
param_names=["x", "y"],
measure_names=["m0", "m1"],
archive_dims=[20, 20],
archive_ranges=[(-1, 1), (-1, 1)],
archive_learning_rate=0.1,
Expand All @@ -70,17 +78,7 @@ if __name__ == "__main__":
emitter_sigma0=0.1,
emitter_batch_size=20,
)
study = optuna.create_study(
sampler=sampler,
directions=[
StudyDirection.MINIMIZE,
# The remaining directions are for the measures, which do not have
# an optimization direction. However, we set MINIMIZE as a
# placeholder direction.
StudyDirection.MINIMIZE,
StudyDirection.MINIMIZE,
],
)
study = optuna.create_study(sampler=sampler)
study.optimize(objective, n_trials=10000)
```

Expand Down
20 changes: 6 additions & 14 deletions package/samplers/cmamae/example.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import optuna
from optuna.study import StudyDirection
import optunahub


module = optunahub.load_module("samplers/cmamae")
CmaMaeSampler = module.CmaMaeSampler


def objective(trial: optuna.trial.Trial) -> tuple[float, float, float]:
def objective(trial: optuna.trial.Trial) -> float:
"""Returns an objective followed by two measures."""
x = trial.suggest_float("x", -10, 10)
y = trial.suggest_float("y", -10, 10)
return x**2 + y**2, x, y
trial.set_user_attr("m0", 2 * x)
trial.set_user_attr("m1", x + y)
return x**2 + y**2


if __name__ == "__main__":
sampler = CmaMaeSampler(
param_names=["x", "y"],
measure_names=["m0", "m1"],
archive_dims=[20, 20],
archive_ranges=[(-1, 1), (-1, 1)],
archive_learning_rate=0.1,
Expand All @@ -29,15 +31,5 @@ def objective(trial: optuna.trial.Trial) -> tuple[float, float, float]:
emitter_sigma0=0.1,
emitter_batch_size=20,
)
study = optuna.create_study(
sampler=sampler,
directions=[
StudyDirection.MINIMIZE,
# The remaining directions are for the measures, which do not have
# an optimization direction. However, we set MINIMIZE as a
# placeholder direction.
StudyDirection.MINIMIZE,
StudyDirection.MINIMIZE,
],
)
study = optuna.create_study(sampler=sampler)
study.optimize(objective, n_trials=10000)
37 changes: 31 additions & 6 deletions package/samplers/cmamae/sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ class CmaMaeSampler(optunahub.samplers.SimpleBaseSampler):
However, it is possible to implement many variations of CMA-MAE and other
quality diversity algorithms using pyribs.

Note that this sampler assumes the objective function will return a list of
values. The first value will be the objective, and the remaining values will
be the measures.
Note that this sampler assumes the measures are set to user_attrs of each trial.
To do so, please call ``trial.set_user_attr("YOUR MEASURE NAME", measure_value)`` for each
measure.

Args:
param_names: List of names of parameters to optimize.
measure_names: List of names of measures.
archive_dims: Number of archive cells in each dimension of the measure
space, e.g. ``[20, 30, 40]`` indicates there should be 3 dimensions
with 20, 30, and 40 cells. (The number of dimensions is implicitly
Expand All @@ -67,6 +68,7 @@ def __init__(
self,
*,
param_names: list[str],
measure_names: list[str],
nabenabe0928 marked this conversation as resolved.
Show resolved Hide resolved
archive_dims: list[int],
archive_ranges: list[tuple[float, float]],
archive_learning_rate: float,
Expand All @@ -77,7 +79,13 @@ def __init__(
emitter_batch_size: int,
) -> None:
self._validate_params(param_names, emitter_x0)
self._param_names = param_names[:]
self._param_names = param_names.copy()
self._measure_names = measure_names.copy()
if len(set(self._measure_names)) != 2:
raise ValueError(
"measure_names must be a list of two unique measure names, "
f"but got measure_names={measure_names}."
)

# NOTE: SimpleBaseSampler must know Optuna search_space information.
search_space = {name: FloatDistribution(-1e9, 1e9) for name in self._param_names}
Expand Down Expand Up @@ -182,8 +190,25 @@ def after_trial(
# Store the trial result.
direction0 = study.directions[0]
minimize_in_optuna = direction0 == StudyDirection.MINIMIZE
assert values is not None, "MyPy redefinition."
modified_values = list([float(v) for v in values])
if values is None:
raise RuntimeError(
f"{self.__class__.__name__} does not support Failed trials, "
f"but trial#{trial.number} failed."
)
user_attrs = trial.user_attrs
if any(measure_name not in user_attrs for measure_name in self._measure_names):
raise KeyError(
f"All of measures in measure_names={self._measure_names} must be set to "
"trial.user_attrs. Please call `trial.set_user_attr(<measure_name>, <value>)` "
"for each measure in your objective function."
)

self._raise_error_if_multi_objective(study)
modified_values = [
float(values[0]),
float(user_attrs[self._measure_names[0]]),
float(user_attrs[self._measure_names[1]]),
]
if minimize_in_optuna:
# The direction of the first objective (pyribs maximizes).
modified_values[0] = -values[0]
Expand Down