OpenEvolve: An Open Source Implementation of Google DeepMind's AlphaEvolve
Harnessing LLMs for Evolutionary Algorithm Discovery

Introduction
Google DeepMind recently announced AlphaEvolve, an evolutionary coding agent that uses Large Language Models to optimize code through an iterative process. This system has been used to make breakthroughs in mathematical algorithms and optimize critical computing infrastructure.
Today, I'm excited to introduce OpenEvolve, an open-source implementation of the AlphaEvolve system that brings these capabilities to the broader research and development community. This article will walk through the architecture of OpenEvolve, showcase two successful examples where we've replicated AlphaEvolve's results, and provide insights on how you can use it for your own projects.
Background: Evolutionary Coding Agents
The idea of evolving code isn't new - genetic programming has been around for decades. However, the combination of modern LLMs with evolutionary techniques creates a powerful new paradigm.
AlphaEvolve represents a significant advancement in this field by:
- Using LLMs to generate sophisticated code modifications
- Evaluating these modifications with automated metrics
- Using an evolutionary framework to improve promising solutions
- Evolving entire codebases, not just individual functions
OpenEvolve implements these principles in a flexible, configurable open-source package.
Technical Architecture
OpenEvolve follows an evolutionary approach with four key components:
- Prompt Sampler: Creates context-rich prompts containing past programs, their scores, and problem descriptions
- LLM Ensemble: Generates code modifications via an ensemble of language models
- Evaluator Pool: Tests generated programs and assigns scores
- Program Database: Stores programs and their evaluation metrics, guiding future evolution
The controller orchestrates interactions between these components in an asynchronous pipeline, maximizing throughput to evaluate as many candidate solutions as possible.
Key Features
- Evolution of entire code files (not just single functions)
- Support for multiple programming languages
- Multi-objective optimization
- Flexible prompt engineering
- Distributed evaluation
- LLM ensemble approach
- Checkpoint system for resuming runs
Example 1: Circle Packing
One of the problems tackled in the AlphaEvolve paper was packing 26 circles of varying sizes into a unit square to maximize the sum of their radii. Google reported achieving a sum of 2.635.
We replicated this experiment using OpenEvolve, targeting the same objective. Our implementation evolved through multiple stages:
Initial Solution
# Initial attempt
# Place a large circle in the center
centers[0] = [0.5, 0.5]
# Place 8 circles around it in a ring
for i in range(8):
angle = 2 * np.pi * i / 8
centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
# Place 16 more circles in an outer ring
for i in range(16):
angle = 2 * np.pi * i / 16
centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
This simple approach produced a sum of approximately 1.87.
Evolution to Generation 10
By generation 10, OpenEvolve had discovered a more sophisticated hexagonal arrangement:
# Generation 10
# Parameters for the arrangement (fine-tuned)
r_center = 0.1675 # Central circle radius
# 1. Place central circle
centers[0] = [0.5, 0.5]
radii[0] = r_center
# 2. First ring: 6 circles in hexagonal arrangement
r_ring1 = 0.1035
ring1_distance = r_center + r_ring1 + 0.0005 # Small gap for stability
for i in range(6):
angle = 2 * np.pi * i / 6
centers[i+1] = [
0.5 + ring1_distance * np.cos(angle),
0.5 + ring1_distance * np.sin(angle)
]
radii[i+1] = r_ring1
This improved the sum to approximately 2.18.
Generation 100: Grid-Based Approach
By generation 100, OpenEvolve had pivoted to a grid-based strategy:
# Generation 100
# Row 1: 5 circles
centers[0] = [0.166, 0.166]
centers[1] = [0.333, 0.166]
centers[2] = [0.500, 0.166]
centers[3] = [0.667, 0.166]
centers[4] = [0.834, 0.166]
# Row 2: 6 circles (staggered)
centers[5] = [0.100, 0.333]
centers[6] = [0.266, 0.333]
# ... additional circles
This grid pattern achieved a sum of approximately 2.32.
Final Solution: Mathematical Optimization
The breakthrough came when OpenEvolve discovered the power of mathematical optimization:
# Final solution with scipy.optimize
def construct_packing():
# ... initialization code ...
# Objective function: Negative sum of radii (to maximize)
def objective(x):
centers = x[:2*n].reshape(n, 2)
radii = x[2*n:]
return -np.sum(radii)
# Constraint: No overlaps and circles stay within the unit square
def constraint(x):
centers = x[:2*n].reshape(n, 2)
radii = x[2*n:]
# Overlap constraint
overlap_constraints = []
for i in range(n):
for j in range(i + 1, n):
dist = np.sqrt(np.sum((centers[i] - centers[j])**2))
overlap_constraints.append(dist - (radii[i] + radii[j]))
# ... boundary constraints ...
# Optimization using SLSQP
result = minimize(objective, x0, method='SLSQP', bounds=bounds,
constraints=constraints)
This approach achieved a sum of 2.634, matching the AlphaEvolve paper's result of 2.635 to within 0.04%!
Final packing arrangement with a sum of radii = 2.634
Two-Phase Approach
We structured the evolution in two phases:
Phase 1: Initial Exploration
- Focus on different fundamental approaches
- Constructor-based pattern exploration
- Population size: 60
- Islands: 4
- Exploitation ratio: 0.7
Phase 2: Breaking Through the Plateau
- Focus on radical innovation
- Increased population size to 70
- Islands: 5
- Reduced exploitation ratio to 0.6
- Updated system prompt to suggest optimization techniques
This two-phase strategy proved crucial for breaking through plateaus in the solution space.
Example 2: Function Minimization
Our second example focused on minimizing a complex non-convex function:
f(x, y) = sin(x) * cos(y) + sin(x*y) + (x^2 + y^2)/20
The global minimum is approximately at (-1.704, 0.678) with a value of -1.519.
Initial Algorithm: Random Search
We started with a simple random search:
def search_algorithm(iterations=1000, bounds=(-5, 5)):
# Initialize with a random point
best_x = np.random.uniform(bounds[0], bounds[1])
best_y = np.random.uniform(bounds[0], bounds[1])
best_value = evaluate_function(best_x, best_y)
for _ in range(iterations):
# Simple random search
x = np.random.uniform(bounds[0], bounds[1])
y = np.random.uniform(bounds[0], bounds[1])
value = evaluate_function(x, y)
if value < best_value:
best_value = value
best_x, best_y = x, y
return best_x, best_y, best_value
Evolved Algorithm: Simulated Annealing
Through evolution, OpenEvolve discovered a simulated annealing algorithm:
def simulated_annealing(bounds=(-5, 5), iterations=1000, step_size=0.1,
initial_temperature=100, cooling_rate=0.99):
# Initialize with a random point
best_x = np.random.uniform(bounds[0], bounds[1])
best_y = np.random.uniform(bounds[0], bounds[1])
best_value = evaluate_function(best_x, best_y)
current_x, current_y = best_x, best_y
current_value = best_value
temperature = initial_temperature
for _ in range(iterations):
# Perturb the current solution
new_x = current_x + np.random.uniform(-step_size, step_size)
new_y = current_y + np.random.uniform(-step_size, step_size)
# Ensure the new solution is within bounds
new_x = max(bounds[0], min(new_x, bounds[1]))
new_y = max(bounds[0], min(new_y, bounds[1]))
new_value = evaluate_function(new_x, new_y)
# Calculate the acceptance probability
if new_value < current_value:
current_x, current_y = new_x, new_y
current_value = new_value
if new_value < best_value:
best_x, best_y = new_x, new_y
best_value = new_value
else:
probability = np.exp((current_value - new_value) / temperature)
if np.random.rand() < probability:
current_x, current_y = new_x, new_y
current_value = new_value
# Cool down the temperature
temperature *= cooling_rate
return best_x, best_y, best_value
The evolved algorithm discovered key optimization concepts:
- Local search with small perturbations
- Temperature-based acceptance to escape local minima
- Cooling schedule for exploration-exploitation balance
- Parameter tuning for optimal performance
LLM Performance Analysis
A critical factor in OpenEvolve's success is the LLM ensemble. We found:
- Low latency is essential - We need many generations, so fast inference is crucial
- Model diversity helps - Different models have different strengths
- Best combination: Gemini-Flash-2.0-lite + Gemini-Flash-2.0 for most tasks
- Cerebras AI's API provided the fastest inference speeds
- For circle packing: Gemini-Flash-2.0 + Claude-Sonnet-3.7 worked best
The key insight: for evolutionary code optimization, you need a balance of speed and quality. Using a faster model for most generations with occasional calls to a more powerful model provides the best results.
How to Use OpenEvolve
Getting started with OpenEvolve is straightforward:
Installation
git clone https://github.com/codelion/openevolve.git
cd openevolve
pip install -e .
Using the Python API
from openevolve import OpenEvolve
# Initialize the system
evolve = OpenEvolve(
initial_program_path="path/to/initial_program.py",
evaluation_file="path/to/evaluator.py",
config_path="path/to/config.yaml"
)
# Run the evolution
best_program = await evolve.run(iterations=1000)
print(f"Best program metrics:")
for name, value in best_program.metrics.items():
print(f" {name}: {value:.4f}")
Using the Command Line
python openevolve-run.py path/to/initial_program.py path/to/evaluator.py \
--config path/to/config.yaml --iterations 1000
Key Configuration Parameters
# Example configuration
max_iterations: 1000
llm:
primary_model: "gemini-2.0-flash-lite"
secondary_model: "gemini-2.0-flash"
temperature: 0.7
database:
population_size: 500
num_islands: 5
exploitation_ratio: 0.7
Preparing Your Own Problems
- Mark code sections to evolve with
# EVOLVE-BLOCK-START
and# EVOLVE-BLOCK-END
comments - Create an evaluation function that returns a dictionary of metrics
- Configure OpenEvolve with appropriate parameters
- Run the evolution process
Future Directions
While OpenEvolve already demonstrates impressive capabilities, there are several exciting directions for future development:
- Supporting more LLM providers through standardized interfaces
- Improved prompt engineering with automatic adaptation
- More sophisticated evaluation cascades for faster pruning of poor solutions
- GUI-based monitoring and control of the evolution process
- Expanded example library covering more domains
Conclusion
OpenEvolve brings the power of evolutionary algorithm discovery to the open-source community. By combining LLMs with automated evaluation in an evolutionary framework, it enables the discovery of novel algorithms across domains.
Our replication of AlphaEvolve's circle packing and function minimization examples demonstrates the system's effectiveness. The ability to transform simple implementations into sophisticated algorithms - like evolving from concentric rings to mathematical optimization, or from random search to simulated annealing - showcases the power of guided evolutionary search.
We invite you to try OpenEvolve, contribute to its development, and apply it to your own algorithmic challenges. The code is available on GitHub at https://github.com/codelion/openevolve.
Resources
- GitHub Repository: https://github.com/codelion/openevolve
- Circle Packing Example: https://github.com/codelion/openevolve/tree/main/examples/circle_packing
- Function Minimization Example: https://github.com/codelion/openevolve/tree/main/examples/function_minimization
- AlphaEvolve Paper: Google DeepMind Blog
If you enjoyed this article, please consider starring the repository and contributing to the project!