Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions openevolve/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
ProgramState,
)
from openevolve.utils.code_utils import calculate_edit_distance
from openevolve.utils.metrics_utils import safe_numeric_average, get_fitness_score
from openevolve.utils.metrics_utils import get_fitness_score, safe_numeric_average

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -1136,6 +1136,7 @@ def _llm_judge_novelty(self, program: Program, similar_program: Program) -> bool
Use LLM to judge if a program is novel compared to a similar existing program
"""
import asyncio

from openevolve.novelty_judge import NOVELTY_SYSTEM_MSG, NOVELTY_USER_MSG

user_msg = NOVELTY_USER_MSG.format(
Expand Down Expand Up @@ -1811,8 +1812,14 @@ def _sample_inspirations(
for _ in range(remaining_slots * 3): # Try more times to find nearby programs
# Perturb coordinates
perturbed_coords = [
max(0, min(self.feature_bins - 1, c + random.randint(-2, 2)))
for c in feature_coords
max(
0,
min(
self.feature_bins_per_dim.get(dim, self.feature_bins) - 1,
c + random.randint(-2, 2),
),
)
for dim, c in zip(self.config.feature_dimensions, feature_coords)
]

cell_key = self._feature_coords_to_key(perturbed_coords)
Expand Down
47 changes: 47 additions & 0 deletions tests/test_inspiration_feature_bounds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Neighborhood sampling must use the bounds of each MAP-Elites dimension."""

import unittest
from unittest.mock import patch

from openevolve.config import DatabaseConfig
from openevolve.database import Program, ProgramDatabase


class TestInspirationFeatureBounds(unittest.TestCase):
def test_neighbor_in_wide_dimension_is_selected_before_random_fallback(self):
db = ProgramDatabase(
DatabaseConfig(
in_memory=True,
num_islands=1,
feature_dimensions=["x", "y"],
feature_bins={"x": 20, "y": 2},
elite_selection_ratio=0.2,
)
)
db.feature_stats = {
dim: {"min": 0.0, "max": 1.0, "values": [0.0, 1.0]} for dim in ("x", "y")
}
parent = Program(id="parent", code="parent", metrics={"score": 0.1, "x": 0.9, "y": 0.75})
neighbor = Program(
id="neighbor", code="neighbor", metrics={"score": 0.2, "x": 0.99, "y": 0.75}
)
best = Program(id="best", code="best", metrics={"score": 1.0, "x": 0.0, "y": 0.0})
far = Program(id="far", code="far", metrics={"score": 0.3, "x": 0.0, "y": 0.75})
db.programs = {p.id: p for p in (parent, neighbor, best, far)}
db.islands = [set(db.programs)]
db.island_best_programs = [best.id]
self.assertEqual(db._calculate_feature_coords(parent), [18, 1])
self.assertEqual(db._calculate_feature_coords(neighbor), [19, 1])

with (
patch("openevolve.database.random.randint", return_value=2),
patch("openevolve.database.random.sample", return_value=[far.id]) as fallback,
):
inspirations = db._sample_inspirations(parent, n=2, island_id=0)

self.assertEqual([p.id for p in inspirations], [best.id, neighbor.id])
fallback.assert_not_called()


if __name__ == "__main__":
unittest.main()
Loading