Skip to content

Commit 31a6dcf

Browse files
authored
Merge pull request #98 from codelion/feat-add-seed-for-reproduciblity
Add deterministic random seed handling for reproducibility
2 parents 7e6e58e + 664e867 commit 31a6dcf

File tree

7 files changed

+44
-6
lines changed

7 files changed

+44
-6
lines changed

configs/default_config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ max_iterations: 1000 # Maximum number of evolution iterations
77
checkpoint_interval: 50 # Save checkpoints every N iterations
88
log_level: "INFO" # Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
99
log_dir: null # Custom directory for logs (default: output_dir/logs)
10-
random_seed: null # Random seed for reproducibility (null = random)
10+
random_seed: 42 # Random seed for reproducibility (null = random, 42 = default)
1111

1212
# Evolution settings
1313
diff_based_evolution: true # Use diff-based evolution (true) or full rewrites (false)

openevolve/config.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ class LLMModelConfig:
3232
timeout: int = None
3333
retries: int = None
3434
retry_delay: int = None
35+
36+
# Reproducibility
37+
random_seed: Optional[int] = None
3538

3639

3740
@dataclass
@@ -97,6 +100,7 @@ def __post_init__(self):
97100
"timeout": self.timeout,
98101
"retries": self.retries,
99102
"retry_delay": self.retry_delay,
103+
"random_seed": self.random_seed,
100104
}
101105
self.update_model_params(shared_config)
102106

@@ -165,7 +169,7 @@ class DatabaseConfig:
165169
migration_rate: float = 0.1 # Fraction of population to migrate
166170

167171
# Random seed for reproducible sampling
168-
random_seed: Optional[int] = None
172+
random_seed: Optional[int] = 42
169173

170174
# Artifact storage
171175
artifacts_base_path: Optional[str] = None # Defaults to db_path/artifacts
@@ -212,7 +216,7 @@ class Config:
212216
checkpoint_interval: int = 100
213217
log_level: str = "INFO"
214218
log_dir: Optional[str] = None
215-
random_seed: Optional[int] = None
219+
random_seed: Optional[int] = 42
216220

217221
# Component configurations
218222
llm: LLMConfig = field(default_factory=LLMConfig)
@@ -256,6 +260,10 @@ def from_dict(cls, config_dict: Dict[str, Any]) -> "Config":
256260
config.prompt = PromptConfig(**config_dict["prompt"])
257261
if "database" in config_dict:
258262
config.database = DatabaseConfig(**config_dict["database"])
263+
264+
# Ensure database inherits the random seed if not explicitly set
265+
if config.database.random_seed is None and config.random_seed is not None:
266+
config.database.random_seed = config.random_seed
259267
if "evaluator" in config_dict:
260268
config.evaluator = EvaluatorConfig(**config_dict["evaluator"])
261269

openevolve/controller.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,27 @@ def __init__(
104104
if self.config.random_seed is not None:
105105
import random
106106
import numpy as np
107+
import hashlib
107108

109+
# Set global random seeds
108110
random.seed(self.config.random_seed)
109111
np.random.seed(self.config.random_seed)
112+
113+
# Create hash-based seeds for different components
114+
base_seed = str(self.config.random_seed).encode('utf-8')
115+
llm_seed = int(hashlib.md5(base_seed + b'llm').hexdigest()[:8], 16) % (2**31)
116+
117+
# Propagate seed to LLM configurations
118+
self.config.llm.random_seed = llm_seed
119+
for model_cfg in self.config.llm.models:
120+
if not hasattr(model_cfg, 'random_seed') or model_cfg.random_seed is None:
121+
model_cfg.random_seed = llm_seed
122+
for model_cfg in self.config.llm.evaluator_models:
123+
if not hasattr(model_cfg, 'random_seed') or model_cfg.random_seed is None:
124+
model_cfg.random_seed = llm_seed
125+
110126
logger.info(f"Set random seed to {self.config.random_seed} for reproducibility")
127+
logger.debug(f"Generated LLM seed: {llm_seed}")
111128

112129
# Load initial program
113130
self.initial_program_path = initial_program_path

openevolve/llm/ensemble.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ def __init__(self, models_cfg: List[LLMModelConfig]):
2727
self.weights = [model.weight for model in models_cfg]
2828
total = sum(self.weights)
2929
self.weights = [w / total for w in self.weights]
30+
31+
# Set up random state for deterministic model selection
32+
self.random_state = random.Random()
33+
# Initialize with seed from first model's config if available
34+
if models_cfg and hasattr(models_cfg[0], 'random_seed') and models_cfg[0].random_seed is not None:
35+
self.random_state.seed(models_cfg[0].random_seed)
36+
logger.debug(f"LLMEnsemble: Set random seed to {models_cfg[0].random_seed} for deterministic model selection")
3037

3138
logger.info(
3239
f"Initialized LLM ensemble with models: "
@@ -50,7 +57,7 @@ async def generate_with_context(
5057

5158
def _sample_model(self) -> LLMInterface:
5259
"""Sample a model from the ensemble based on weights"""
53-
index = random.choices(range(len(self.models)), weights=self.weights, k=1)[0]
60+
index = self.random_state.choices(range(len(self.models)), weights=self.weights, k=1)[0]
5461
return self.models[index]
5562

5663
async def generate_multiple(self, prompt: str, n: int, **kwargs) -> List[str]:

openevolve/llm/openai.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def __init__(
3232
self.retry_delay = model_cfg.retry_delay
3333
self.api_base = model_cfg.api_base
3434
self.api_key = model_cfg.api_key
35+
self.random_seed = getattr(model_cfg, 'random_seed', None)
3536

3637
# Set up API client
3738
self.client = openai.OpenAI(
@@ -73,6 +74,11 @@ async def generate_with_context(
7374
"top_p": kwargs.get("top_p", self.top_p),
7475
"max_tokens": kwargs.get("max_tokens", self.max_tokens),
7576
}
77+
78+
# Add seed parameter for reproducibility if configured
79+
seed = kwargs.get("seed", self.random_seed)
80+
if seed is not None:
81+
params["seed"] = seed
7682

7783
# Attempt the API call with retries
7884
retries = kwargs.get("retries", self.retries)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "openevolve"
7-
version = "0.0.8"
7+
version = "0.0.9"
88
description = "Open-source implementation of AlphaEvolve"
99
readme = "README.md"
1010
requires-python = ">=3.9"

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
setup(
44
name="openevolve",
5-
version="0.0.8",
5+
version="0.0.9",
66
packages=find_packages(),
77
include_package_data=True,
88
)

0 commit comments

Comments
 (0)