Skip to content

Commit b5f4349

Browse files
committed
Enhance MAP-Elites feature binning and diversity handling
This update adds per-dimension feature bin support, adaptive feature scaling, and a diversity reference set with caching for MAP-Elites in ProgramDatabase. The config now allows feature_bins to be an int or dict, and includes diversity_reference_size. Related tests and evaluator logic are updated for improved cascade validation and feature map coverage.
1 parent 9cd6464 commit b5f4349

File tree

10 files changed

+930
-409
lines changed

10 files changed

+930
-409
lines changed

openevolve/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Version information for openevolve package."""
22

3-
__version__ = "0.0.19"
3+
__version__ = "0.0.20"

openevolve/config.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,10 @@ class DatabaseConfig:
166166
diversity_metric: str = "edit_distance" # Options: "edit_distance", "feature_based"
167167

168168
# Feature map dimensions for MAP-Elites
169-
# feature_dimensions: List[str] = field(default_factory=lambda: ["score", "complexity"])
170-
feature_dimensions: List[str] = field(default_factory=lambda: ["complexity"])
171-
feature_bins: int = 10
169+
# Default to complexity and diversity for better exploration
170+
feature_dimensions: List[str] = field(default_factory=lambda: ["complexity", "diversity"])
171+
feature_bins: Union[int, Dict[str, int]] = 10 # Can be int (all dims) or dict (per-dim)
172+
diversity_reference_size: int = 20 # Size of reference set for diversity calculation
172173

173174
# Migration parameters for island-based evolution
174175
migration_interval: int = 50 # Migrate every N generations

openevolve/database.py

Lines changed: 361 additions & 149 deletions
Large diffs are not rendered by default.

openevolve/evaluator.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def _load_evaluation_function(self) -> None:
8989

9090
self.evaluate_function = module.evaluate
9191
logger.info(f"Successfully loaded evaluation function from {self.evaluation_file}")
92-
92+
9393
# Validate cascade configuration
9494
self._validate_cascade_configuration(module)
9595
except Exception as e:
@@ -99,16 +99,16 @@ def _load_evaluation_function(self) -> None:
9999
def _validate_cascade_configuration(self, module) -> None:
100100
"""
101101
Validate cascade evaluation configuration and warn about potential issues
102-
102+
103103
Args:
104104
module: The loaded evaluation module
105105
"""
106106
if self.config.cascade_evaluation:
107107
# Check if cascade functions exist
108108
has_stage1 = hasattr(module, "evaluate_stage1")
109-
has_stage2 = hasattr(module, "evaluate_stage2")
109+
has_stage2 = hasattr(module, "evaluate_stage2")
110110
has_stage3 = hasattr(module, "evaluate_stage3")
111-
111+
112112
if not has_stage1:
113113
logger.warning(
114114
f"Configuration has 'cascade_evaluation: true' but evaluator "
@@ -123,7 +123,9 @@ def _validate_cascade_configuration(self, module) -> None:
123123
f"multi-stage evaluation for better cascade benefits."
124124
)
125125
else:
126-
logger.debug(f"Cascade evaluation properly configured with available stage functions")
126+
logger.debug(
127+
f"Cascade evaluation properly configured with available stage functions"
128+
)
127129

128130
async def evaluate_program(
129131
self,
@@ -305,7 +307,9 @@ def get_pending_artifacts(self, program_id: str) -> Optional[Dict[str, Union[str
305307
"""
306308
return self._pending_artifacts.pop(program_id, None)
307309

308-
async def _direct_evaluate(self, program_path: str) -> Union[Dict[str, float], EvaluationResult]:
310+
async def _direct_evaluate(
311+
self, program_path: str
312+
) -> Union[Dict[str, float], EvaluationResult]:
309313
"""
310314
Directly evaluate a program using the evaluation function with timeout
311315
@@ -616,22 +620,23 @@ async def _llm_evaluate(self, program_code: str, program_id: str = "") -> Dict[s
616620
def _create_cascade_error_context(self, stage: str, error: Exception) -> dict:
617621
"""
618622
Create rich error context for cascade failures
619-
623+
620624
Args:
621625
stage: The stage where the error occurred
622626
error: The exception that was raised
623-
627+
624628
Returns:
625629
Dictionary with enhanced error context
626630
"""
627631
import time
632+
628633
return {
629634
"failure_stage": stage,
630635
"error_type": type(error).__name__,
631636
"error_message": str(error),
632637
"timestamp": time.time(),
633638
"cascade_config": self.config.cascade_evaluation,
634-
"cascade_thresholds": getattr(self.config, 'cascade_thresholds', []),
639+
"cascade_thresholds": getattr(self.config, "cascade_thresholds", []),
635640
"timeout_config": self.config.timeout,
636641
"evaluation_file": self.evaluation_file,
637642
}

0 commit comments

Comments
 (0)