Skip to content

Commit 602b368

Browse files
committed
Improve MAP-Elites feature dimension handling
Clarifies default and custom feature dimensions in documentation and config files. Updates ProgramDatabase to raise an error if a specified feature dimension is missing from program metrics, with a helpful message. Adds a test to ensure missing feature dimensions trigger the correct error.
1 parent d93c772 commit 602b368

File tree

5 files changed

+96
-7
lines changed

5 files changed

+96
-7
lines changed

README.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ OpenEvolve implements a comprehensive evolutionary coding system with:
3636
- **Island-Based Evolution**: Multiple populations with periodic migration for diversity maintenance
3737
- **Inspiration vs Performance**: Sophisticated prompt engineering separating top performers from diverse inspirations
3838
- **Multi-Strategy Selection**: Elite, diverse, and exploratory program sampling strategies
39+
- **Adaptive Feature Dimensions**: Default features (complexity & diversity) with customizable multi-dimensional search spaces
3940

4041
#### 📊 **Evaluation & Feedback Systems**
4142
- **Artifacts Side-Channel**: Capture build errors, profiling data, and execution feedback for LLM improvement
@@ -274,7 +275,7 @@ database:
274275
population_size: 500
275276
num_islands: 5 # Island-based evolution
276277
migration_interval: 20
277-
feature_dimensions: ["score", "complexity"] # Quality-diversity features
278+
feature_dimensions: ["complexity", "diversity"] # Default quality-diversity features
278279
279280
evaluator:
280281
# Advanced evaluation features
@@ -293,8 +294,41 @@ Sample configuration files are available in the `configs/` directory:
293294
- `default_config.yaml`: Comprehensive configuration with all available options
294295
- `island_config_example.yaml`: Advanced island-based evolution setup
295296

297+
### Feature Dimensions in MAP-Elites
298+
299+
Feature dimensions control how programs are organized in the MAP-Elites quality-diversity grid:
300+
301+
**Default Features**: If `feature_dimensions` is NOT specified in your config, OpenEvolve uses `["complexity", "diversity"]` as defaults.
302+
303+
**Built-in Features** (always computed internally by OpenEvolve):
304+
- **complexity**: Code length (recommended default)
305+
- **diversity**: Code structure diversity compared to other programs (recommended default)
306+
307+
Only `complexity` and `diversity` are used as defaults because they work well across all program types.
308+
309+
**Custom Features**: You can mix built-in features with metrics from your evaluator:
310+
```yaml
311+
database:
312+
feature_dimensions: ["complexity", "performance", "correctness"] # Mix of built-in and custom
313+
# Per-dimension bin configuration (optional)
314+
feature_bins:
315+
complexity: 10 # 10 bins for complexity
316+
performance: 20 # 20 bins for performance (from YOUR evaluator)
317+
correctness: 15 # 15 bins for correctness (from YOUR evaluator)
318+
```
319+
320+
**Important**: OpenEvolve will raise an error if a specified feature is not found in the evaluator's metrics. This ensures your configuration is correct. The error message will show available metrics to help you fix the configuration.
321+
296322
See the [Configuration Guide](configs/default_config.yaml) for a full list of options.
297323

324+
### Default Metric for Program Selection
325+
326+
When comparing and selecting programs, OpenEvolve uses the following priority:
327+
1. **combined_score**: If your evaluator returns a `combined_score` metric, it will be used as the primary fitness measure
328+
2. **Average of all metrics**: If no `combined_score` is provided, OpenEvolve calculates the average of all numeric metrics returned by your evaluator
329+
330+
This ensures programs can always be compared even without explicit fitness definitions. For best results, consider having your evaluator return a `combined_score` that represents overall program fitness.
331+
298332
## Artifacts Channel
299333

300334
OpenEvolve includes an **artifacts side-channel** that allows evaluators to capture build errors, profiling results, etc. to provide better feedback to the LLM in subsequent generations. This feature enhances the evolution process by giving the LLM context about what went wrong and how to fix it.

configs/default_config.yaml

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,32 @@ database:
9090
# Note: diversity_metric is fixed to "edit_distance" (feature_based not implemented)
9191

9292
# Feature map dimensions for MAP-Elites
93+
# Default if not specified: ["complexity", "diversity"]
94+
#
95+
# Built-in features (always available, computed by OpenEvolve):
96+
# - "complexity": Code length
97+
# - "diversity": Code structure diversity
98+
#
99+
# You can mix built-in features with custom metrics from your evaluator:
93100
feature_dimensions: # Dimensions for MAP-Elites feature map
94-
- "score" # Performance score
95-
- "complexity" # Code complexity (length)
101+
- "complexity" # Code length (built-in)
102+
- "diversity" # Code diversity (built-in)
103+
# Example with custom features:
104+
# feature_dimensions:
105+
# - "performance" # Must be returned by your evaluator
106+
# - "correctness" # Must be returned by your evaluator
107+
# - "memory_efficiency" # Must be returned by your evaluator
108+
109+
# Number of bins per dimension
110+
# Can be a single integer (same for all dimensions) or a dict
96111
feature_bins: 10 # Number of bins per dimension
112+
# Example of per-dimension configuration:
113+
# feature_bins:
114+
# complexity: 10 # 10 bins for complexity
115+
# diversity: 15 # 15 bins for diversity
116+
# performance: 20 # 20 bins for custom metric
117+
118+
diversity_reference_size: 20 # Size of reference set for diversity calculation
97119

98120
# Evaluator configuration
99121
evaluator:

configs/island_config_example.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,14 @@ database:
3333
# Note: diversity_metric fixed to "edit_distance"
3434

3535
# Feature map dimensions for MAP-Elites
36-
feature_dimensions: ["score", "complexity"]
36+
# Default if not specified: ["complexity", "diversity"]
37+
# Comment out the line below to use the defaults
38+
# feature_dimensions: ["complexity", "diversity"]
3739
feature_bins: 10
40+
# Can also use per-dimension bins:
41+
# feature_bins:
42+
# performance: 20
43+
# correctness: 10
3844

3945
# Prompt configuration
4046
prompt:

openevolve/database.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -721,9 +721,12 @@ def _calculate_feature_coords(self, program: Program) -> List[int]:
721721
bin_idx = max(0, min(num_bins - 1, bin_idx))
722722
coords.append(bin_idx)
723723
else:
724-
# Default to middle bin if feature not found
725-
num_bins = self.feature_bins_per_dim.get(dim, self.feature_bins)
726-
coords.append(num_bins // 2)
724+
# Feature not found - this is an error
725+
raise ValueError(
726+
f"Feature dimension '{dim}' specified in config but not found in program metrics. "
727+
f"Available metrics: {list(program.metrics.keys())}. "
728+
f"Either remove '{dim}' from feature_dimensions or ensure your evaluator returns it."
729+
)
727730
# Only log coordinates at debug level for troubleshooting
728731
logger.debug(
729732
"MAP-Elites coords: %s",

tests/test_map_elites_features.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,30 @@ def test_feature_coordinates_with_new_defaults(self):
244244
self.assertGreaterEqual(coord, 0)
245245
self.assertLess(coord, db.feature_bins)
246246

247+
def test_missing_feature_dimension_error(self):
248+
"""Test that missing feature dimensions raise appropriate errors"""
249+
config = Config()
250+
config.database.in_memory = True
251+
config.database.feature_dimensions = ["complexity", "nonexistent_metric"]
252+
db = ProgramDatabase(config.database)
253+
254+
# Add a program without the required metric
255+
program = Program(
256+
id="test_error",
257+
code="def test(): pass",
258+
language="python",
259+
metrics={"score": 0.5}, # Missing 'nonexistent_metric'
260+
)
261+
262+
# Should raise ValueError when calculating feature coordinates
263+
with self.assertRaises(ValueError) as context:
264+
db.add(program)
265+
266+
# Check error message
267+
self.assertIn("nonexistent_metric", str(context.exception))
268+
self.assertIn("not found in program metrics", str(context.exception))
269+
self.assertIn("score", str(context.exception)) # Should show available metrics
270+
247271

248272
if __name__ == "__main__":
249273
unittest.main()

0 commit comments

Comments
 (0)