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

Implement CMA-MAE archive thresholds #256

Merged
merged 60 commits into from Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
6518d27
revive old implementation of add_single (w/o numba)
itsdawei Aug 24, 2022
bf492ff
add threshold
itsdawei Aug 24, 2022
010edbe
fix dtype issue
itsdawei Aug 24, 2022
0ea8db8
remove print
itsdawei Aug 25, 2022
5f68ed6
Merge branch 'master' of github.com:icaros-usc/pyribs into implement-…
itsdawei Aug 25, 2022
ee663ac
implement threshold for batch_addition
itsdawei Aug 26, 2022
6ccb6b2
add cma_mae to sphere.py
itsdawei Aug 26, 2022
c2644e7
add passive archvie
itsdawei Aug 29, 2022
3d49034
Use improvement ranker
itsdawei Aug 30, 2022
b5f6587
history
itsdawei Aug 31, 2022
4c605ca
tried to fix batch add threshold
itsdawei Aug 31, 2022
354386a
fix batch add
itsdawei Aug 31, 2022
76bfaf6
fix add batch
itsdawei Aug 31, 2022
ccbf832
fix batch add bug
itsdawei Aug 31, 2022
b68f09c
fix requested changes
itsdawei Aug 31, 2022
c781cda
add threshold update test
itsdawei Sep 4, 2022
7530023
fix pytest bugs
itsdawei Sep 4, 2022
08c6a1d
Add tests for add with single solutions; fix bugs
itsdawei Sep 17, 2022
393b0ea
raise error when new threhold is nan.
itsdawei Sep 17, 2022
4554be0
history
itsdawei Sep 17, 2022
86cda6c
More detailed scheduler error msg
btjanaka Sep 28, 2022
67f6e72
Batch threshold computation
btjanaka Oct 4, 2022
41750f2
fix requested changes
itsdawei Oct 11, 2022
3f44000
Merge branch 'master' of github.com:icaros-usc/pyribs into implement-…
itsdawei Oct 11, 2022
cfb997f
fix error merge conflict
itsdawei Oct 11, 2022
105f2ef
add github action tests
itsdawei Oct 11, 2022
0887238
Simplify examples.sh by removing redundence default argument.
itsdawei Oct 11, 2022
b086120
Tweak comments
btjanaka Oct 12, 2022
26424c2
Format strings
btjanaka Oct 12, 2022
4e31815
Merge branch 'implement-archive-thresholds' of github.com:icaros-usc/…
btjanaka Oct 12, 2022
8d6eb28
Choose new sphere tests
btjanaka Oct 12, 2022
d52a1b3
Remove print
btjanaka Oct 12, 2022
93940a3
Change test name
btjanaka Oct 12, 2022
d3edccd
more naming, ignore pylint
btjanaka Oct 12, 2022
05f28de
More complex multiple cell threshold test
btjanaka Oct 12, 2022
0a27c77
Formatting
btjanaka Oct 12, 2022
d2e0755
Simplify test fixture
btjanaka Oct 12, 2022
160c10f
Fix: change old_objective to old_threshold
itsdawei Oct 12, 2022
3b95c3f
Merge branch 'implement-archive-thresholds' of github.com:icaros-usc/…
itsdawei Oct 12, 2022
8dcdb54
docstring
itsdawei Oct 12, 2022
6f28831
Rename test
btjanaka Oct 12, 2022
04a074e
Merge branch 'implement-archive-thresholds' of github.com:icaros-usc/…
btjanaka Oct 12, 2022
3b0506d
Fix add_single
btjanaka Oct 12, 2022
2fe3287
add test; typos
itsdawei Oct 12, 2022
ebf7243
Merge branch 'implement-archive-thresholds' of github.com:icaros-usc/…
itsdawei Oct 13, 2022
82dbb49
Add inf and nan checks
btjanaka Oct 13, 2022
f9b2fef
Merge branch 'implement-archive-thresholds' of github.com:icaros-usc/…
btjanaka Oct 13, 2022
d3d9b9e
Add test for nonfinite inputs
btjanaka Oct 13, 2022
f8a1f89
Finite checks in add
btjanaka Oct 13, 2022
4feac42
Error messages
btjanaka Oct 13, 2022
d53dceb
Test add single with threshold updates
btjanaka Oct 13, 2022
28c163e
Note on add_single
btjanaka Oct 13, 2022
9186028
Revert sphere outdir name
btjanaka Oct 13, 2022
d2eeac6
TODO
btjanaka Oct 13, 2022
5d66944
Modify err msg
btjanaka Oct 13, 2022
025807b
Grammar
btjanaka Oct 13, 2022
e308c39
Update add_batch implementation
btjanaka Oct 16, 2022
50b7e7b
Documentation and tidying up
btjanaka Oct 17, 2022
4349b0f
More tests; fix bugs
btjanaka Oct 17, 2022
fda24bc
Comment
btjanaka Oct 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions HISTORY.md
Expand Up @@ -6,6 +6,9 @@

#### API

- Implement CMA-MAE archive thresholds (#256)
- Revive the old implementation of `add_single` removed in (#221)
itsdawei marked this conversation as resolved.
Show resolved Hide resolved
- Add separate tests for `add_single` and `add` with single solution
- Fix all examples and tutorials (#253)
- Add restart timer to `EvolutionStrategyEmitter` and `GradientAborescenceEmitter`(#255)
- Rename fields and update documentation (#249, #250)
Expand Down
2 changes: 2 additions & 0 deletions docs/tutorials.md
Expand Up @@ -7,6 +7,7 @@ tutorials/lunar_lander
tutorials/lsi_mnist
tutorials/arm_repertoire
tutorials/fooling_mnist
tutorials/cma_mae
```

Tutorials are Python notebooks with detailed explanations of pyribs usage. They
Expand All @@ -23,3 +24,4 @@ needs for execution.
| {doc}`tutorials/lsi_mnist` | {class}`~ribs.archives.GridArchive` | {class}`~ribs.emitters.EvolutionStrategyEmitter` | {class}`~ribs.schedulers.Scheduler` |
| {doc}`tutorials/arm_repertoire` | {class}`~ribs.archives.CVTArchive` | {class}`~ribs.emitters.EvolutionStrategyEmitter` | {class}`~ribs.schedulers.Scheduler` |
| {doc}`tutorials/fooling_mnist` | {class}`~ribs.archives.GridArchive` | {class}`~ribs.emitters.GaussianEmitter` | {class}`~ribs.schedulers.Scheduler` |
| {doc}`tutorials/cma_mae` | {class}`~ribs.archives.GridArchive` | {class}`~ribs.emitters.EvolutionStrategyEmitter` | {class}`~ribs.schedulers.Scheduler` |
168 changes: 126 additions & 42 deletions examples/sphere.py
Expand Up @@ -34,18 +34,22 @@
- `cma_mega`: GridArchive with GradientAborescenceEmitter.
- `cma_mega_adam`: GridArchive with GradientAborescenceEmitter using Adam
Optimizer.

Note: the settings for `cma_mega` and `cma_mega_adam` are consistent with the
paper (`Fontaine 2021 <https://arxiv.org/abs/2106.03894>`_) in which these
algorithms are proposed.
- `cma_mae`: GridArchive (learning_rate = 0.01) with EvolutionStrategyEmitter
using ImprovementRanker.
- `cma_maega`: GridArchive (learning_rate = 0.01) with
GradientAborescenceEmitter using ImprovementRanker.

All algorithms use 15 emitters, each with a batch size of 37. Each one runs for
4500 iterations for a total of 15 * 37 * 4500 ~= 2.5M evaluations.

Note that the CVTArchive in this example uses 10,000 cells, as opposed to the
250,000 (500x500) in the GridArchive, so it is not fair to directly compare
`cvt_map_elites` and `line_cvt_map_elites` to the other algorithms. However, the
other algorithms may be fairly compared because they use the same archive.
Notes:
- `cma_mega` and `cma_mega_adam` use only one emitter and run for 10,000
iterations. This is to be consistent with the paper (`Fontaine 2021
<https://arxiv.org/abs/2106.03894>`_) in which these algorithms were proposed.
- `cma_mae` and `cma_maega` run for 10,000 iterations as well.
- CVTArchive in this example uses 10,000 cells, as opposed to the 250,000
(500x500) in the GridArchive, so it is not fair to directly compare
`cvt_map_elites` and `line_cvt_map_elites` to the other algorithms.

Outputs are saved in the `sphere_output/` directory by default. The archive is
saved as a CSV named `{algorithm}_{dim}_archive.csv`, while snapshots of the
Expand Down Expand Up @@ -142,46 +146,58 @@ def sphere(solution_batch):
)


def create_scheduler(algorithm, dim, seed):
def create_scheduler(algorithm,
solution_dim,
archive_dims,
learning_rate,
use_result_archive=True,
seed=None):
"""Creates a scheduler based on the algorithm name.

Args:
algorithm (str): Name of the algorithm passed into sphere_main.
dim (int): Dimensionality of the sphere function.
solution_dim(int): Dimensionality of the sphere function.
archive_dims (int): Dimensionality of the archive.
learning_rate (float): Learning rate of archive.
use_result_archive (bool): Whether to use a separate archive to store
the results.
seed (int): Main seed or the various components.
Returns:
scheduler: A ribs scheduler for running the algorithm.
ribs.schedulers.Scheduler: A ribs scheduler for running the algorithm.
"""
max_bound = dim / 2 * 5.12
max_bound = solution_dim / 2 * 5.12
bounds = [(-max_bound, max_bound), (-max_bound, max_bound)]
initial_sol = np.zeros(dim)
initial_sol = np.zeros(solution_dim)
batch_size = 37
num_emitters = 15
mode = "batch"
threshold_min = -np.inf # default

if algorithm in ["cma_mae", "cma_maega"]:
threshold_min = 0

# Create archive.
if algorithm in [
"map_elites", "line_map_elites", "cma_me_imp", "cma_me_imp_mu",
"cma_me_rd", "cma_me_rd_mu", "cma_me_opt", "cma_me_mixed"
]:
archive = GridArchive(solution_dim=dim,
dims=(500, 500),
ranges=bounds,
seed=seed)
elif algorithm in ["cvt_map_elites", "line_cvt_map_elites"]:
archive = CVTArchive(solution_dim=dim,
if algorithm in ["cvt_map_elites", "line_cvt_map_elites"]:
archive = CVTArchive(solution_dim=solution_dim,
cells=10_000,
ranges=bounds,
samples=100_000,
use_kd_tree=True)
elif algorithm in ["cma_mega", "cma_mega_adam"]:
# Note that the archive is smaller for these algorithms. This is to be
# consistent with Fontaine 2021 <https://arxiv.org/abs/2106.03894>.
archive = GridArchive(solution_dim=dim,
dims=(100, 100),
else:
archive = GridArchive(solution_dim=solution_dim,
dims=archive_dims,
ranges=bounds,
learning_rate=learning_rate,
threshold_min=threshold_min,
seed=seed)
else:
raise ValueError(f"Algorithm `{algorithm}` is not recognized")

# Create result archive.
result_archive = None
if use_result_archive:
result_archive = GridArchive(solution_dim=solution_dim,
dims=archive_dims,
ranges=bounds,
seed=seed)

# Create emitters. Each emitter needs a different seed, so that they do not
# all do the same thing.
Expand Down Expand Up @@ -278,7 +294,38 @@ def create_scheduler(algorithm, dim, seed):
batch_size=batch_size - 1, # 1 solution is returned by ask_dqd
seed=emitter_seeds[0])
]
return Scheduler(archive, emitters)
elif algorithm == "cma_mae":
emitters = [
EvolutionStrategyEmitter(
archive=archive,
x0=initial_sol,
sigma0=0.5,
ranker="imp",
selection_rule="mu",
restart_rule="basic",
batch_size=batch_size,
seed=s,
) for s in emitter_seeds
]
elif algorithm in ["cma_maega"]:
emitters = [
GradientAborescenceEmitter(archive,
initial_sol,
sigma0=10.0,
step_size=1.0,
ranker="imp",
grad_opt="gradient_ascent",
restart_rule="basic",
bounds=None,
batch_size=batch_size,
seed=s) for s in emitter_seeds
]

print(
f"Created Scheduler for {algorithm} with learning rate {learning_rate} "
f"and add mode {mode}, using solution dim {solution_dim} and archive "
f"dims {archive_dims}.")
return Scheduler(archive, emitters, result_archive, add_mode=mode)


def save_heatmap(archive, heatmap_path):
Expand All @@ -304,47 +351,82 @@ def save_heatmap(archive, heatmap_path):
def sphere_main(algorithm,
dim=None,
itrs=None,
archive_dims=None,
learning_rate=None,
outdir="sphere_output",
log_freq=250,
seed=None):
"""Demo on the Sphere function.

Args:
algorithm (str): Name of the algorithm.
itsdawei marked this conversation as resolved.
Show resolved Hide resolved
dim (int): Dimensionality of solutions.
dim (int): Dimensionality of the sphere function.
itrs (int): Iterations to run.
archive_dims (tuple): Dimensionality of the archive.
learning_rate (float): The archive learning rate.
outdir (str): Directory to save output.
log_freq (int): Number of iterations to wait before recording metrics
and saving heatmap.
seed (int): Seed for the algorithm. By default, there is no seed.
"""
# Use default dim for each algorithm.
if dim is None:
if algorithm in ["cma_mega", "cma_mega_adam"]:
if algorithm in ["cma_mega", "cma_mega_adam", "cma_maega"]:
dim = 1_000
elif algorithm in ["cma_mae"]:
dim = 100
elif algorithm in [
"map_elites", "line_map_elites", "cma_me_imp", "cma_me_imp_mu",
"cma_me_rd", "cma_me_rd_mu", "cma_me_opt", "cma_me_mixed"
]:
dim = 20

# Use default itrs for each algorithm.
if itrs is None:
if algorithm in ["cma_mega", "cma_mega_adam"]:
if algorithm in ["cma_mega", "cma_mega_adam", "cma_mae", "cma_maega"]:
itrs = 10_000
elif algorithm in [
"map_elites", "line_map_elites", "cma_me_imp", "cma_me_imp_mu",
"cma_me_rd", "cma_me_rd_mu", "cma_me_opt", "cma_me_mixed"
]:
itrs = 4500

# Use default archive_dim for each algorithm.
if archive_dims is None:
if algorithm in ["cma_mega", "cma_mega_adam", "cma_mae", "cma_maega"]:
archive_dims = (100, 100)
elif algorithm in [
"map_elites", "line_map_elites", "cma_me_imp", "cma_me_imp_mu",
"cma_me_rd", "cma_me_rd_mu", "cma_me_opt", "cma_me_mixed"
]:
archive_dims = (500, 500)

# Use default learning_rate for each algorithm.
if learning_rate is None:
if algorithm in ["cma_mae", "cma_maega"]:
learning_rate = 0.01
elif algorithm in [
"map_elites", "line_map_elites", "cma_me_imp", "cma_me_imp_mu",
"cma_me_rd", "cma_me_rd_mu", "cma_me_opt", "cma_me_mixed",
"cma_mega", "cma_mega_adam"
]:
learning_rate = 1.0

name = f"{algorithm}_{dim}"
outdir = Path(outdir)
if not outdir.is_dir():
outdir.mkdir()

is_dqd = algorithm in ['cma_mega', 'cma_mega_adam']
is_dqd = algorithm in ["cma_mega", "cma_mega_adam", "cma_maega"]
use_result_archive = algorithm in ["cma_mae", "cma_maega"]

scheduler = create_scheduler(algorithm, dim, seed)
archive = scheduler.archive
scheduler = create_scheduler(algorithm,
dim,
archive_dims,
learning_rate,
use_result_archive=use_result_archive,
seed=seed)
result_archive = scheduler.result_archive
metrics = {
"QD Score": {
"x": [0],
Expand All @@ -357,7 +439,7 @@ def sphere_main(algorithm,
}

non_logging_time = 0.0
save_heatmap(archive, str(outdir / f"{name}_heatmap_{0:05d}.png"))
save_heatmap(result_archive, str(outdir / f"{name}_heatmap_{0:05d}.png"))

for itr in tqdm.trange(1, itrs + 1):
itr_start = time.time()
Expand All @@ -380,19 +462,21 @@ def sphere_main(algorithm,
final_itr = itr == itrs
if itr % log_freq == 0 or final_itr:
if final_itr:
archive.as_pandas(include_solutions=final_itr).to_csv(
result_archive.as_pandas(include_solutions=final_itr).to_csv(
outdir / f"{name}_archive.csv")

# Record and display metrics.
metrics["QD Score"]["x"].append(itr)
metrics["QD Score"]["y"].append(archive.stats.qd_score)
metrics["QD Score"]["y"].append(result_archive.stats.qd_score)
metrics["Archive Coverage"]["x"].append(itr)
metrics["Archive Coverage"]["y"].append(archive.stats.coverage)
metrics["Archive Coverage"]["y"].append(
result_archive.stats.coverage)
print(f"Iteration {itr} | Archive Coverage: "
f"{metrics['Archive Coverage']['y'][-1] * 100:.3f}% "
f"QD Score: {metrics['QD Score']['y'][-1]:.3f}")

save_heatmap(archive, str(outdir / f"{name}_heatmap_{itr:05d}.png"))
save_heatmap(result_archive,
str(outdir / f"{name}_heatmap_{itr:05d}.png"))

# Plot metrics.
print(f"Algorithm Time (Excludes Logging and Setup): {non_logging_time}s")
Expand Down
35 changes: 35 additions & 0 deletions examples/tutorials/cma_mae.ipynb
@@ -0,0 +1,35 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "69e0c236-9407-496f-be0d-9562f8191ce4",
"metadata": {},
"source": [
"# CMA-MAE and Archive Thresholds\n",
"\n",
"Coming soon!"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
14 changes: 14 additions & 0 deletions ribs/_utils.py
@@ -1,4 +1,18 @@
"""Miscellaneous internal utilities."""
import numpy as np


def check_finite(x, name):
"""Checks that x is finite (i.e. not infinity or NaN).

`x` must be either a scalar or NumPy array.
"""
if not np.all(np.isfinite(x)):
if np.isscalar(x):
raise ValueError(f"{name} must be finite (infinity "
"and NaN values are not supported).")
raise ValueError(f"All elements of {name} must be finite (infinity "
"and NaN values are not supported).")


def check_batch_shape(array, array_name, dim, dim_name, extra_msg=""):
Expand Down