Optimization framework using Large Language Models
Project description
Optiverse
Optiverse is a Python library designed for evolving code and algorithms using Large Language Models (LLMs). Inspired by Deepmind's AlphaEvolve, it provides a flexible framework to iteratively improve programs, from code snippets to full files, in any programming language.
With Optiverse, you define a problem and provide an evaluator. The system then generates and evolves candidate solutions over multiple iterations, learning which approaches yield better results.
📖 Read the announcement post: Optiverse: Evolving Code with LLMs
Table of Contents
Why Optiverse?
Optiverse helps developers and researchers automate code improvement by evolving and refining solutions with LLMs. Key capabilities include:
- Whole-file evolution: Unlike other implementations limited to isolated code blocks, Optiverse evolves entire source files.
- Modular architecture: Swap out search strategies easily to experiment with different optimization approaches.
- Multi-language support: As long as you provide an evaluator for your problem, Optiverse can optimize code in any programming language.
- Flexible LLM integration: Works with any LLM provider compatible with the OpenAI API standard, such as OpenAI, Google Gemini, NVIDIA, and more.
Quick Start
Follow these steps to set up Optiverse and run an example.
1. Set Up Environment
First, create a virtual environment and install dependencies:
make init
Then, activate the virtual environment:
source venv/bin/activate
2. Run the Traveling Salesman Problem example:
This example uses Optiverse to solve the Traveling Salesman Problem (TSP). The code is in the examples/tsp directory.
The example supports multiple LLM providers through environment variables:
LLM_API_KEY: Your API key for the chosen providerLLM_MODEL: The model name to useLLM_PROVIDER: The provider name (openai,google, ornvidia)
Example with Google Gemini:
LLM_API_KEY="your-gemini-api-key" LLM_MODEL="gemini-2.0-flash" LLM_PROVIDER="google" make run.tsp
Note: Optiverse uses the OpenAI package under the hood, so it supports any LLM provider that follows the OpenAI API standard.
Sample Output:
When you run it, you'll see output like this:
2025-07-17 09:35:47 - optiverse.optimizer - INFO - Evaluating and saving initial solution...
2025-07-17 09:35:47 - examples.tsp.evaluator - INFO - Score 1: 33800.20318013358
2025-07-17 09:35:47 - examples.tsp.evaluator - INFO - Score 2: 32719.233809839046
2025-07-17 09:35:48 - examples.tsp.evaluator - INFO - Score 3: 35557.94658416552
2025-07-17 09:35:48 - optiverse.optimizer - INFO - Initial solution saved with ID: d3c1a4eb294e469593b52ee805d7028b, score: 34025.79452471271
2025-07-17 09:35:48 - optiverse.optimizer - INFO - Starting iteration 1/100
2025-07-17 09:35:50 - httpx - INFO - HTTP Request: POST https://generativelanguage.googleapis.com/v1beta/openai/chat/completions "HTTP/1.1 200 OK"
2025-07-17 09:35:51 - optiverse.solution_generator - INFO - No code blocks found in LLM response
2025-07-17 09:35:51 - optiverse.optimizer - INFO - No code generated by solution generator
2025-07-17 09:35:51 - optiverse.optimizer - INFO - Starting iteration 2/100
2025-07-17 09:35:52 - httpx - INFO - HTTP Request: POST https://generativelanguage.googleapis.com/v1beta/openai/chat/completions "HTTP/1.1 200 OK"
2025-07-17 09:35:58 - optiverse.optimizer - INFO - Evaluating solution
2025-07-17 09:36:09 - examples.tsp.evaluator - INFO - Score 1: 3149.0239046268334
2025-07-17 09:36:21 - examples.tsp.evaluator - INFO - Score 2: 3200.6735649311436
2025-07-17 09:36:30 - examples.tsp.evaluator - INFO - Score 3: 3089.4221942253616
2025-07-17 09:36:30 - optiverse.optimizer - INFO - Evaluation result: 3146.3732212611126
2025-07-17 09:36:30 - optiverse.optimizer - INFO - Saved solution with ID: 564d577d222a42ba8e1defb0dd2870e3
...
Understanding the Results
During optimization, Optiverse saves results in directories named tmp/YYYYMMDD_HHMM, indicating the date and time of each run.
solutions.csv
Inside each run directory, solutions.csv provides a high-level overview of all solutions explored:
| id | score | t_group | t_move | t_parent_id_1 | ... |
|---|---|---|---|---|---|
| 0d8c5789dde24a94901871c18d6d9854 | 2817.0555492637 | 1 | exploitation | 034afd2da8894ab6838efb17bc28f201 | ... |
| a42574709f27484fade7adc76683b2cf | 2828.6215589938 | 6 | exploitation | 96c7140054154a20a4b67fd986658dd4 | ... |
| 883f6de275c14b32822feb5cdaaba55d | 2837.3500311898 | 6 | exploitation | a42574709f27484fade7adc76683b2cf | ... |
Individual Solution Directories
Each solution has a dedicated directory named after its ID, containing:
prompt.md: The prompt sent to the LLM.solution.txt: The code generated by the LLM.description.txt: The LLM's own explanation of the changes it made.metadata.json: Metadata including ID and score.*_stdout.txtand*_strerr.txt: Program logs for debugging.
Evolved Solutions
Traveling Salesman Problem (TSP)
Problem Summary
The Traveling Salesman Problem (TSP) requires finding the shortest possible route visiting each city exactly once and returning to the start. It’s a standard benchmark for combinatorial optimization algorithms.
The full problem description is in examples/tsp/problem.md.
Initial Solution
The initial solution simply generates a random permutation of cities:
from datetime import timedelta
import random
from context import Context
def solve(context: Context) -> None:
num_cities = len(context.instance)
while context.remaining_time() > timedelta():
# Generate a random solution (permutation of city indices)
solution = random.sample(range(num_cities), num_cities)
context.report_new_best_solution(solution)
break # since it's pointless to continue in this example
Evolved Algorithm
Through iterative evolution, Optiverse transformed the initial solution into an Iterated Local Search (ILS) variant, including:
- Intensification (improvement phase): 2-opt local search with delta evaluation for efficient improvements.
- Diversification (perturbation phase): Perturbation operators such as ruin-and-recreate, block moves, swaps, and double bridge moves.
- Performance optimizations: Precomputed distance matrix for faster evaluation.
Evolution Setup
- Iterations: ~300
- LLM model: Qwen3-235B-A22B
Evaluation Results
On a 280-city instance (3 runs of 30 seconds each), the evolved algorithm achieved an average tour length of 2593, within ~0.5% of the known optimal (2579).
Full Evolved Code
import random
import math
from datetime import timedelta
from typing import List, Tuple
from context import Context
def compute_tour_length(tour: List[int], dist_matrix: List[List[float]]) -> float:
length = 0.0
n = len(tour)
for i in range(n):
a, b = tour[i], tour[(i+1)%n]
length += dist_matrix[a][b]
return length
def nearest_neighbor(instance: List[Tuple[float, float]], start: int, dist_matrix: List[List[float]], nearest_neighbors: List[List[int]]) -> List[int]:
n = len(instance)
visited = [False]*n
tour, current = [start], start
visited[start] = True
while len(tour) < n:
for city in nearest_neighbors[current]:
if not visited[city]:
next_city = city
break
tour.append(next_city)
visited[next_city] = True
current = next_city
return tour
def two_opt(tour: List[int], dist_matrix: List[List[float]]) -> List[int]:
tour = tour.copy()
n = len(tour)
improved = True
while improved:
improved = False
for i in range(n-1):
for j in range(i+2, n):
a, b = tour[i], tour[i+1]
c, d = tour[j-1], tour[j]
if dist_matrix[a][b] + dist_matrix[c][d] > dist_matrix[a][c] + dist_matrix[b][d]:
tour[i+1:j] = tour[i+1:j][::-1]
improved = True
return tour
def solve(context: Context) -> None:
instance = context.instance
n = len(instance)
if n <= 1:
context.report_new_best_solution(list(range(n)))
return
dist_matrix = [[math.hypot(x1-x2, y1-y2) for x2, y2 in instance] for x1, y1 in instance]
nearest_neighbors = []
for u in range(n):
sorted_cities = sorted(range(n), key=lambda c: dist_matrix[u][c])
nearest_neighbors.append(sorted_cities)
best_solution, best_length = None, float('inf')
start_points = [0, n//4, n//2, (3*n)//4, n-1]
if n >= 8:
start_points += random.sample(range(n), 3)
for start in start_points:
if context.remaining_time() <= timedelta(seconds=1):
break
tour = nearest_neighbor(instance, start, dist_matrix, nearest_neighbors)
optimized = two_opt(tour, dist_matrix)
current_length = compute_tour_length(optimized, dist_matrix)
if current_length < best_length:
best_solution, best_length = optimized, current_length
context.report_new_best_solution(best_solution)
while context.remaining_time() > timedelta(seconds=0.1) and best_solution:
new_solution = best_solution.copy()
move = random.random()
n_cities = len(best_solution)
if move < 0.2: # Ruin and Recreate
remove_count = max(2, int(n_cities * 0.2))
removed = random.sample(best_solution, remove_count)
current_tour = [city for city in best_solution if city not in removed]
for city in removed:
best_pos, best_delta = 0, float('inf')
for i in range(len(current_tour)):
a = current_tour[i]
b = current_tour[(i+1) % len(current_tour)]
delta = dist_matrix[a][city] + dist_matrix[city][b] - dist_matrix[a][b]
if delta < best_delta:
best_delta, best_pos = delta, i
current_tour.insert(best_pos + 1, city)
new_solution = current_tour
elif move < 0.4: # Block move
i = random.randint(0, n_cities-3)
j = random.randint(i+1, n_cities-1)
block = new_solution[i+1:j+1]
if random.random() < 0.5:
block = block[::-1]
new_solution = new_solution[:i+1] + new_solution[j+1:]
k = random.randint(0, len(new_solution)-1)
new_solution = new_solution[:k+1] + block + new_solution[k+1:]
elif move < 0.6: # Swap
i, j = random.sample(range(n_cities), 2)
new_solution[i], new_solution[j] = new_solution[j], new_solution[i]
else: # Double bridge
if n_cities < 4:
i, j = random.sample(range(n_cities), 2)
new_solution[i], new_solution[j] = new_solution[j], new_solution[i]
else:
a, b, c, d = sorted(random.sample(range(n_cities), 4))
new_solution = (new_solution[:a+1] +
new_solution[c+1:d+1] +
new_solution[a+1:b+1][::-1] +
new_solution[b+1:c+1] +
new_solution[d+1:])
optimized = two_opt(new_solution, dist_matrix)
new_length = compute_tour_length(optimized, dist_matrix)
if new_length < best_length:
best_solution, best_length = optimized, new_length
context.report_new_best_solution(best_solution)
if best_solution is None:
solution = list(range(n))
random.shuffle(solution)
context.report_new_best_solution(solution)
else:
final_tour = two_opt(best_solution, dist_matrix)
context.report_new_best_solution(final_tour)
License
Optiverse is open source and licensed under the GNU General Public License v3.0 (GPLv3).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file optiverse-0.0.2.tar.gz.
File metadata
- Download URL: optiverse-0.0.2.tar.gz
- Upload date:
- Size: 16.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4375f7bc22fe86304829fd38eb98e655f6c1ed24b766c57b7b208ffb7c634a3a
|
|
| MD5 |
2d210bff49af896f77eb375f970c1a9d
|
|
| BLAKE2b-256 |
fcc2ca391452f413ffb750b7c7012b07ecd04bbec6e3dd090e88e1404039fe89
|
File details
Details for the file optiverse-0.0.2-py3-none-any.whl.
File metadata
- Download URL: optiverse-0.0.2-py3-none-any.whl
- Upload date:
- Size: 13.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cf6fd11155ec0f1932959933bee933ff2bcb9655cd9d9b6b99c4b97320a73b3d
|
|
| MD5 |
ce96e4c33b27f0ee2ec574a3752f50d3
|
|
| BLAKE2b-256 |
5b80b50d3ff915afaa2830943b8900fa661dbe69cdfc3c7686b669f0e79d5745
|