Skip to content

Commit 8312b0f

Browse files
MashAliKSuhailB
authored andcommitted
init
1 parent 2b988d5 commit 8312b0f

File tree

7 files changed

+317
-242
lines changed

7 files changed

+317
-242
lines changed

openevolve/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ class DatabaseConfig:
151151

152152
# Evolutionary parameters
153153
population_size: int = 1000
154+
allowed_population_overflow: int = 50
154155
archive_size: int = 100
155156
num_islands: int = 5
156157

@@ -217,6 +218,7 @@ class Config:
217218
log_level: str = "INFO"
218219
log_dir: Optional[str] = None
219220
random_seed: Optional[int] = 42
221+
language: str = None
220222

221223
# Component configurations
222224
llm: LLMConfig = field(default_factory=LLMConfig)
@@ -361,4 +363,4 @@ def load_config(config_path: Optional[Union[str, Path]] = None) -> Config:
361363
# Make the system message available to the individual models, in case it is not provided from the prompt sampler
362364
config.llm.update_model_params({"system_message": config.prompt.system_message})
363365

364-
return config
366+
return config

openevolve/controller.py

Lines changed: 101 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,23 @@
55
import asyncio
66
import logging
77
import os
8+
import shutil
89
import re
910
import time
1011
import uuid
1112
from pathlib import Path
1213
from typing import Any, Dict, List, Optional, Tuple, Union
1314
import traceback
15+
import concurrent.futures
1416

1517
from openevolve.config import Config, load_config
1618
from openevolve.database import Program, ProgramDatabase
1719
from openevolve.evaluator import Evaluator
1820
from openevolve.llm.ensemble import LLMEnsemble
1921
from openevolve.prompt.sampler import PromptSampler
22+
from openevolve.iteration import run_iteration_sync, Result
2023
from openevolve.utils.code_utils import (
21-
apply_diff,
2224
extract_code_language,
23-
extract_diffs,
24-
format_diff_summary,
25-
parse_evolve_blocks,
26-
parse_full_rewrite,
2725
)
2826
from openevolve.utils.format_utils import (
2927
format_metrics_safe,
@@ -129,7 +127,8 @@ def __init__(
129127
# Load initial program
130128
self.initial_program_path = initial_program_path
131129
self.initial_program_code = self._load_initial_program()
132-
self.language = extract_code_language(self.initial_program_code)
130+
if not self.config.language:
131+
self.config.language = extract_code_language(self.initial_program_code)
133132

134133
# Extract file extension from initial program
135134
self.file_extension = os.path.splitext(initial_program_path)[1]
@@ -162,8 +161,9 @@ def __init__(
162161
self.evaluator_prompt_sampler,
163162
database=self.database,
164163
)
164+
self.evaluation_file = evaluation_file
165165

166-
logger.info(f"Initialized OpenEvolve with {initial_program_path} " f"and {evaluation_file}")
166+
logger.info(f"Initialized OpenEvolve with {initial_program_path}")
167167

168168
def _setup_logging(self) -> None:
169169
"""Set up logging"""
@@ -236,7 +236,7 @@ async def run(
236236
initial_program = Program(
237237
id=initial_program_id,
238238
code=self.initial_program_code,
239-
language=self.language,
239+
language=self.config.language,
240240
metrics=initial_metrics,
241241
iteration_found=start_iteration,
242242
)
@@ -263,171 +263,112 @@ async def run(
263263
logger.info(f"Using island-based evolution with {self.config.database.num_islands} islands")
264264
self.database.log_island_status()
265265

266-
for i in range(start_iteration, total_iterations):
267-
iteration_start = time.time()
268-
269-
# Manage island evolution - switch islands periodically
270-
if i > start_iteration and current_island_counter >= programs_per_island:
271-
self.database.next_island()
272-
current_island_counter = 0
273-
logger.debug(f"Switched to island {self.database.current_island}")
274-
275-
current_island_counter += 1
276-
277-
# Sample parent and inspirations from current island
278-
parent, inspirations = self.database.sample()
279-
280-
# Get artifacts for the parent program if available
281-
parent_artifacts = self.database.get_artifacts(parent.id)
282-
283-
# Get actual top programs for prompt context (separate from inspirations)
284-
# This ensures the LLM sees only high-performing programs as examples
285-
actual_top_programs = self.database.get_top_programs(5)
286-
287-
# Build prompt
288-
prompt = self.prompt_sampler.build_prompt(
289-
current_program=parent.code,
290-
parent_program=parent.code, # We don't have the parent's code, use the same
291-
program_metrics=parent.metrics,
292-
previous_programs=[p.to_dict() for p in self.database.get_top_programs(3)],
293-
top_programs=[p.to_dict() for p in actual_top_programs], # Use actual top programs
294-
inspirations=[p.to_dict() for p in inspirations], # Pass inspirations separately
295-
language=self.language,
296-
evolution_round=i,
297-
diff_based_evolution=self.config.diff_based_evolution,
298-
program_artifacts=parent_artifacts if parent_artifacts else None,
299-
)
300-
301-
# Generate code modification
302-
try:
303-
llm_response = await self.llm_ensemble.generate_with_context(
304-
system_message=prompt["system"],
305-
messages=[{"role": "user", "content": prompt["user"]}],
266+
# create temp file to save database snapshots to for process workers to load from
267+
temp_db_path = "tmp/" + str(uuid.uuid4())
268+
self.database.save(temp_db_path, start_iteration)
269+
270+
with concurrent.futures.ProcessPoolExecutor(
271+
max_workers=self.config.evaluator.parallel_evaluations
272+
) as executor:
273+
futures = []
274+
for i in range(start_iteration, total_iterations):
275+
futures.append(
276+
executor.submit(
277+
run_iteration_sync, i, self.config, self.evaluation_file, temp_db_path
278+
)
306279
)
307280

308-
# Parse the response
309-
if self.config.diff_based_evolution:
310-
diff_blocks = extract_diffs(llm_response)
311-
312-
if not diff_blocks:
313-
logger.warning(f"Iteration {i+1}: No valid diffs found in response")
281+
iteration = start_iteration + 1
282+
for future in concurrent.futures.as_completed(futures):
283+
logger.info(f"Completed iteration {iteration}")
284+
try:
285+
result: Result = future.result()
286+
# if result is nonType
287+
if not isinstance(result, Result):
288+
logger.warning(f"No valid diffs or program length exceeded limit")
314289
continue
290+
# Manage island evolution - switch islands periodically
291+
if (
292+
iteration - 1 > start_iteration
293+
and current_island_counter >= programs_per_island
294+
):
295+
self.database.next_island()
296+
current_island_counter = 0
297+
logger.debug(f"Switched to island {self.database.current_island}")
298+
299+
current_island_counter += 1
300+
301+
# Add to database (will be added to current island)
302+
self.database.add(result.child_program, iteration=iteration)
303+
304+
# Log prompts
305+
self.database.log_prompt(
306+
template_key=(
307+
"full_rewrite_user" if not self.config.diff_based_evolution else "diff_user"
308+
),
309+
program_id=result.child_program.id,
310+
prompt=result.prompt,
311+
responses=[result.llm_response],
312+
)
315313

316-
# Apply the diffs
317-
child_code = apply_diff(parent.code, llm_response)
318-
changes_summary = format_diff_summary(diff_blocks)
319-
else:
320-
# Parse full rewrite
321-
new_code = parse_full_rewrite(llm_response, self.language)
322-
323-
if not new_code:
324-
logger.warning(f"Iteration {i+1}: No valid code found in response")
325-
continue
326-
327-
child_code = new_code
328-
changes_summary = "Full rewrite"
329-
330-
# Check code length
331-
if len(child_code) > self.config.max_code_length:
332-
logger.warning(
333-
f"Iteration {i+1}: Generated code exceeds maximum length "
334-
f"({len(child_code)} > {self.config.max_code_length})"
314+
# Store artifacts if they exist (after program is added to database)
315+
if result.artifacts:
316+
self.database.store_artifacts(result.child_program.id, result.artifacts)
317+
318+
# Log prompts
319+
self.database.log_prompt(
320+
template_key=(
321+
"full_rewrite_user" if not self.config.diff_based_evolution else "diff_user"
322+
),
323+
program_id=result.child_program.id,
324+
prompt=result.prompt,
325+
responses=[result.llm_response],
335326
)
336-
continue
337327

338-
# Evaluate the child program
339-
child_id = str(uuid.uuid4())
340-
child_metrics = await self.evaluator.evaluate_program(child_code, child_id)
341-
342-
# Handle artifacts if they exist
343-
artifacts = self.evaluator.get_pending_artifacts(child_id)
344-
345-
# Create a child program
346-
child_program = Program(
347-
id=child_id,
348-
code=child_code,
349-
language=self.language,
350-
parent_id=parent.id,
351-
generation=parent.generation + 1,
352-
metrics=child_metrics,
353-
metadata={
354-
"changes": changes_summary,
355-
"parent_metrics": parent.metrics,
356-
},
357-
)
328+
# Increment generation for current island
329+
self.database.increment_island_generation()
358330

359-
# Add to database (will be added to current island)
360-
self.database.add(child_program, iteration=i + 1)
361-
362-
# Log prompts
363-
self.database.log_prompt(
364-
template_key=(
365-
"full_rewrite_user" if not self.config.diff_based_evolution else "diff_user"
366-
),
367-
program_id=child_id,
368-
prompt=prompt,
369-
responses=[llm_response],
370-
)
331+
# Check if migration should occur
332+
if self.database.should_migrate():
333+
logger.info(f"Performing migration at iteration {iteration}")
334+
self.database.migrate_programs()
335+
self.database.log_island_status()
371336

372-
# Store artifacts if they exist
373-
if artifacts:
374-
self.database.store_artifacts(child_id, artifacts)
375-
376-
# Log prompts
377-
self.database.log_prompt(
378-
template_key=(
379-
"full_rewrite_user" if not self.config.diff_based_evolution else "diff_user"
380-
),
381-
program_id=child_id,
382-
prompt=prompt,
383-
responses=[llm_response],
384-
)
337+
# Log progress
338+
self._log_iteration(
339+
iteration, result.parent, result.child_program, result.iteration_time
340+
)
385341

386-
# Increment generation for current island
387-
self.database.increment_island_generation()
388-
389-
# Check if migration should occur
390-
if self.database.should_migrate():
391-
logger.info(f"Performing migration at iteration {i+1}")
392-
self.database.migrate_programs()
393-
self.database.log_island_status()
394-
395-
# Log progress
396-
iteration_time = time.time() - iteration_start
397-
self._log_iteration(i, parent, child_program, iteration_time)
398-
399-
# Specifically check if this is the new best program
400-
if self.database.best_program_id == child_program.id:
401-
logger.info(f"🌟 New best solution found at iteration {i+1}: {child_program.id}")
402-
logger.info(f"Metrics: {format_metrics_safe(child_program.metrics)}")
403-
404-
# Save checkpoint
405-
if (i + 1) % self.config.checkpoint_interval == 0:
406-
self._save_checkpoint(i + 1)
407-
# Also log island status at checkpoints
408-
logger.info(f"Island status at checkpoint {i+1}:")
409-
self.database.log_island_status()
410-
411-
# Check if target score reached
412-
if target_score is not None:
413-
# Only consider numeric metrics for target score calculation
414-
numeric_metrics = [
415-
v
416-
for v in child_metrics.values()
417-
if isinstance(v, (int, float)) and not isinstance(v, bool)
418-
]
419-
if numeric_metrics:
420-
avg_score = sum(numeric_metrics) / len(numeric_metrics)
342+
# Specifically check if this is the new best program
343+
if self.database.best_program_id == result.child_program.id:
344+
logger.info(
345+
f"🌟 New best solution found at iteration {iteration}: {result.child_program.id}"
346+
)
347+
logger.info(f"Metrics: {format_metrics_safe(result.child_program.metrics)}")
348+
349+
# Save checkpoint
350+
if (iteration) % self.config.checkpoint_interval == 0:
351+
self._save_checkpoint(iteration)
352+
# Also log island status at checkpoints
353+
logger.info(f"Island status at checkpoint {iteration}:")
354+
self.database.log_island_status()
355+
356+
# Check if target score reached
357+
if target_score is not None:
358+
avg_score = sum(result["child_metrics"].values()) / max(
359+
1, len(result.child_metrics)
360+
)
421361
if avg_score >= target_score:
422362
logger.info(
423-
f"Target score {target_score} reached after {i+1} iterations"
363+
f"Target score {target_score} reached after {iteration} iterations"
424364
)
425365
break
366+
self.database.save(temp_db_path, iteration)
426367

427-
except Exception as e:
428-
logger.exception(f"Error in iteration {i+1}: {str(e)}")
429-
continue
430-
368+
except Exception as e:
369+
logger.error(f"Error in iteration {i+1}: {str(e)}")
370+
continue
371+
shutil.rmtree(temp_db_path)
431372
# Get the best program using our tracking mechanism
432373
best_program = None
433374
if self.database.best_program_id:
@@ -607,4 +548,4 @@ def _save_best_program(self, program: Optional[Program] = None) -> None:
607548
indent=2,
608549
)
609550

610-
logger.info(f"Saved best program to {code_path} with program info to {info_path}")
551+
logger.info(f"Saved best program to {code_path} with program info to {info_path}")

0 commit comments

Comments
 (0)