From bb0ac0ba26f5fb4226565409698aaac39d599fc5 Mon Sep 17 00:00:00 2001 From: Bryon Tjanaka Date: Tue, 2 Jul 2024 16:32:29 -0700 Subject: [PATCH 1/3] Sort rankers and add DensityRanker --- ribs/emitters/rankers.py | 67 +++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/ribs/emitters/rankers.py b/ribs/emitters/rankers.py index a7b572114..9407d4f96 100644 --- a/ribs/emitters/rankers.py +++ b/ribs/emitters/rankers.py @@ -12,22 +12,26 @@ a ranker, e.g. "imp". The supported abbreviations are: +* ``density``: :class:`DensityRanker` * ``imp``: :class:`ImprovementRanker` -* ``2imp``: :class:`TwoStageImprovementRanker` -* ``rd``: :class:`RandomDirectionRanker` -* ``2rd``: :class:`TwoStageRandomDirectionRanker` +* ``nov``: :class:`NoveltyRanker` * ``obj``: :class:`ObjectiveRanker` +* ``rd``: :class:`RandomDirectionRanker` +* ``2imp``: :class:`TwoStageImprovementRanker` * ``2obj``: :class:`TwoStageObjectiveRanker` +* ``2rd``: :class:`TwoStageRandomDirectionRanker` .. autosummary:: :toctree: + ribs.emitters.rankers.DensityRanker ribs.emitters.rankers.ImprovementRanker - ribs.emitters.rankers.TwoStageImprovementRanker - ribs.emitters.rankers.RandomDirectionRanker - ribs.emitters.rankers.TwoStageRandomDirectionRanker + ribs.emitters.rankers.NoveltyRanker ribs.emitters.rankers.ObjectiveRanker + ribs.emitters.rankers.RandomDirectionRanker + ribs.emitters.rankers.TwoStageImprovementRanker ribs.emitters.rankers.TwoStageObjectiveRanker + ribs.emitters.rankers.TwoStageRandomDirectionRanker ribs.emitters.rankers.RankerBase """ from abc import ABC, abstractmethod @@ -35,12 +39,14 @@ import numpy as np __all__ = [ + "DensityRanker", "ImprovementRanker", - "TwoStageImprovementRanker", - "RandomDirectionRanker", - "TwoStageRandomDirectionRanker", + "NoveltyRanker", "ObjectiveRanker", + "RandomDirectionRanker", + "TwoStageImprovementRanker", "TwoStageObjectiveRanker", + "TwoStageRandomDirectionRanker", "RankerBase", ] @@ -348,7 +354,6 @@ class NoveltyRanker(RankerBase): """ def rank(self, emitter, archive, data, add_info): - # Sort only by objective value. return np.flip(np.argsort(add_info["novelty"])), add_info["novelty"] rank.__doc__ = f""" @@ -358,21 +363,47 @@ def rank(self, emitter, archive, data, add_info): """ +class DensityRanker(RankerBase): + """Ranks solutions based on density in measure space. + + The archive must be a :class:`~ribs.archives.DensityArchive` or have a + ``compute_density`` method. + """ + + def rank(self, emitter, archive, data, add_info): + try: + density = archive.compute_density(data["measures"]) + except AttributeError as e: + raise AttributeError("DensityRanker requires that the archive have" + "a compute_density method.") from e + + # Lower density is better, so we sort as normal (i.e., ascending order). + return np.argsort(density), density + + rank.__doc__ = f""" +Ranks solutions based on density in measure space. + +{_RANK_ARGS} + """ + + _NAME_TO_RANKER_MAP = { + "DensityRanker": DensityRanker, "ImprovementRanker": ImprovementRanker, - "TwoStageImprovementRanker": TwoStageImprovementRanker, - "RandomDirectionRanker": RandomDirectionRanker, - "TwoStageRandomDirectionRanker": TwoStageRandomDirectionRanker, + "NoveltyRanker": NoveltyRanker, "ObjectiveRanker": ObjectiveRanker, + "RandomDirectionRanker": RandomDirectionRanker, + "TwoStageImprovementRanker": TwoStageImprovementRanker, "TwoStageObjectiveRanker": TwoStageObjectiveRanker, - "NoveltyRanker": NoveltyRanker, + "TwoStageRandomDirectionRanker": TwoStageRandomDirectionRanker, + "density": DensityRanker, "imp": ImprovementRanker, - "2imp": TwoStageImprovementRanker, - "rd": RandomDirectionRanker, - "2rd": TwoStageRandomDirectionRanker, + "nov": NoveltyRanker, "obj": ObjectiveRanker, + "rd": RandomDirectionRanker, + "2imp": TwoStageImprovementRanker, "2obj": TwoStageObjectiveRanker, - "nov": NoveltyRanker, + "2rd": TwoStageRandomDirectionRanker, } From 7f6550eea6d966e6f08277b9eb19bf80fdce60ef Mon Sep 17 00:00:00 2001 From: Bryon Tjanaka Date: Tue, 2 Jul 2024 16:44:10 -0700 Subject: [PATCH 2/3] Tests --- tests/emitters/rankers_test.py | 36 +++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/tests/emitters/rankers_test.py b/tests/emitters/rankers_test.py index 17fc99957..2e7d83c22 100644 --- a/tests/emitters/rankers_test.py +++ b/tests/emitters/rankers_test.py @@ -4,8 +4,8 @@ from numpy.testing import assert_allclose from ribs.archives import GridArchive -from ribs.emitters.rankers import (NoveltyRanker, ObjectiveRanker, - RandomDirectionRanker, +from ribs.emitters.rankers import (DensityRanker, NoveltyRanker, + ObjectiveRanker, RandomDirectionRanker, TwoStageImprovementRanker, TwoStageObjectiveRanker, TwoStageRandomDirectionRanker) @@ -199,9 +199,8 @@ def test_two_stage_objective_ranker(archive_fixture, emitter): ]).all() -def test_novelty_ranker(archive_fixture): - _, x0 = archive_fixture - solution_batch = [x0, x0, x0, x0] +def test_novelty_ranker(): + solution_batch = [[1, 2, 3]] * 4 measures_batch = [[0, 0], [1.2, 1.2], [0.1, 0.1], [1.5, 1.5]] ranker = NoveltyRanker() @@ -219,3 +218,30 @@ def test_novelty_ranker(archive_fixture): assert (indices == [0, 2, 1, 3]).all() assert_allclose(ranking_values, [1.0, 0.5, 0.9, 0.4]) + + +def test_density_ranker(): + solution_batch = [[1, 2, 3]] * 4 + measures_batch = [[0, 0], [1.2, 1.2], [0.1, 0.1], [1.5, 1.5]] + + class FakeDensityArchive: + """Mock density archive for testing.""" + + def compute_density(self, + measures): # pylint: disable = unused-argument + """Returns fake densities for the four solutions.""" + return [0.5, 0.3, 0.7, 0.1] + + ranker = DensityRanker() + indices, ranking_values = ranker.rank( + emitter=None, + archive=FakeDensityArchive(), + data={ + "solution": solution_batch, + "measures": measures_batch, + }, + add_info={}, + ) + + assert (indices == [3, 1, 0, 2]).all() + assert_allclose(ranking_values, [0.5, 0.3, 0.7, 0.1]) From 842517c0bfdc1e1ec5a91130fd44cd11be1f6fe9 Mon Sep 17 00:00:00 2001 From: Bryon Tjanaka Date: Tue, 2 Jul 2024 16:46:34 -0700 Subject: [PATCH 3/3] history --- HISTORY.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index c3aa215b7..408741492 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,7 @@ #### API +- Add DensityRanker for density descent search ({pr}`483`) - Add NoveltyRanker for novelty search ({pr}`477`) - Add proximity_archive_plot for visualizing ProximityArchive ({pr}`476`, {pr}`480`) @@ -19,7 +20,7 @@ #### Documentation -- Add novelty search with CMA-ES to sphere example ({pr}`478`) +- Add novelty search with CMA-ES to sphere example ({pr}`478`, {pr}`482`) #### Improvements