Introducing a "threshold-elites" variant alongside map-elites #408
+133
−2
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Net effect (what changes for users)
You can now choose between map-elites and threshold-elites using DatabaseConfig.variant.
In threshold-elites, programs are:
filtered by novelty,
grouped into islands (with parent island inheritance),
and locally compared by embedding distance with a winner-takes-all rule inside a threshold radius.
openevolve/database.py
This is the main functional change: introduces a “threshold-elites” variant alongside map-elites, and refactors add() accordingly.
Variant-based dispatch in add(...)
Database.add(...) now checks config.variant:
"map-elites" → add_map_elites_program(...)
"threshold-elites" → add_threshold_elites_program(...)
otherwise raises NotImplementedError.
New add_threshold_elites_program(...) behavior
When adding a program:
Stores program + iteration bookkeeping
Computes embedding + an elite score
Calls _calculate_feature_coords(program) and stores in program.embedding.
Computes a fitness-based score = get_fitness_score(...).
Stores program.metrics["elite_score"] = score.
Determines island placement
If target_island is not given and the program has a parent, it inherits the parent’s island from parent.metadata["island"] to keep island isolation.
Else falls back to self.current_island.
Novelty gating happens before insertion
Threshold-elites competition (within an island)
For existing programs in the island, computes Euclidean distance between embeddings.
If distance < elite_threshold, it keeps the higher elite_score and demotes the other by setting its elite_score to -inf (effectively marking it as dominated).
Finalization
Exploration parent sampling now preserves embeddings
In two places inside _sample_exploration_parent(...), when cloning the best program, it now does:
This prevents the clone from sharing the same embedding object reference.
Misc
openevolve/config.py
Extends DatabaseConfig with parameters to support a Quality-Diversity variant:
variant: str = "map-elites" (variant selector)
elite_threshold: int = 3 (distance threshold used by the threshold-elites variant)