fastevolve 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastevolve/__init__.py +11 -0
- fastevolve/config.py +14 -0
- fastevolve/controller.py +86 -0
- fastevolve/evaluator/__init__.py +5 -0
- fastevolve/evaluator/config.py +9 -0
- fastevolve/evaluator/evaluator.py +26 -0
- fastevolve/evaluator/result.py +13 -0
- fastevolve/llm_ensemble/__init__.py +6 -0
- fastevolve/llm_ensemble/base.py +6 -0
- fastevolve/llm_ensemble/config.py +20 -0
- fastevolve/llm_ensemble/ensemble.py +18 -0
- fastevolve/llm_ensemble/ollama.py +45 -0
- fastevolve/program_database/__init__.py +6 -0
- fastevolve/program_database/config.py +11 -0
- fastevolve/program_database/database.py +57 -0
- fastevolve/program_database/embedder.py +25 -0
- fastevolve/program_database/program.py +16 -0
- fastevolve/prompt_sampler/__init__.py +5 -0
- fastevolve/prompt_sampler/config.py +9 -0
- fastevolve/prompt_sampler/sampler.py +12 -0
- fastevolve/prompt_sampler/template_library.py +25 -0
- fastevolve/telemetry.py +36 -0
- fastevolve-0.2.0.dist-info/METADATA +46 -0
- fastevolve-0.2.0.dist-info/RECORD +26 -0
- fastevolve-0.2.0.dist-info/WHEEL +4 -0
- fastevolve-0.2.0.dist-info/entry_points.txt +2 -0
fastevolve/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from importlib.metadata import PackageNotFoundError, version as _pkg_version
|
|
2
|
+
|
|
3
|
+
from .config import Config
|
|
4
|
+
from .controller import Controller, RunResult, apply_diff
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
__version__ = _pkg_version("fastevolve")
|
|
8
|
+
except PackageNotFoundError:
|
|
9
|
+
__version__ = "0.0.0+unknown"
|
|
10
|
+
|
|
11
|
+
__all__ = ["Config", "Controller", "RunResult", "apply_diff", "__version__"]
|
fastevolve/config.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from .prompt_sampler.config import PromptConfig
|
|
3
|
+
from .llm_ensemble.config import EnsembleConfig
|
|
4
|
+
from .evaluator.config import EvaluatorConfig
|
|
5
|
+
from .program_database.config import DatabaseConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(kw_only=True)
|
|
9
|
+
class Config:
|
|
10
|
+
prompt: PromptConfig = field(default_factory=PromptConfig)
|
|
11
|
+
ensemble: EnsembleConfig = field(default_factory=EnsembleConfig)
|
|
12
|
+
evaluator: EvaluatorConfig = field(default_factory=EvaluatorConfig)
|
|
13
|
+
database: DatabaseConfig = field(default_factory=DatabaseConfig)
|
|
14
|
+
iterations: int = 100
|
fastevolve/controller.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from rich.progress import (
|
|
6
|
+
BarColumn, MofNCompleteColumn, Progress, TextColumn,
|
|
7
|
+
TimeElapsedColumn, TimeRemainingColumn,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from .config import Config
|
|
11
|
+
from .prompt_sampler import PromptSampler, TemplateLibrary
|
|
12
|
+
from .llm_ensemble import LLMEnsemble
|
|
13
|
+
from .evaluator import Evaluator
|
|
14
|
+
from .program_database import ProgramDatabase, Program
|
|
15
|
+
from .telemetry import setup, span, timings, log
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(kw_only=True)
|
|
19
|
+
class RunResult:
|
|
20
|
+
best: Optional[Program]
|
|
21
|
+
iterations: int
|
|
22
|
+
stats: dict = field(default_factory=dict)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def apply_diff(parent_program: Program, diff: str) -> Program:
|
|
26
|
+
m = re.search(r"```(?:python)?\s*\n(.*?)```", diff, re.DOTALL)
|
|
27
|
+
code = (m.group(1) if m else diff).strip()
|
|
28
|
+
if not code:
|
|
29
|
+
code = parent_program.code
|
|
30
|
+
return Program(code=code, parent_id=parent_program.id, island=parent_program.island,
|
|
31
|
+
generation=parent_program.generation + 1)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Controller:
|
|
35
|
+
def __init__(self, config: Config, *, initial_program: str):
|
|
36
|
+
setup()
|
|
37
|
+
self.config = config
|
|
38
|
+
lib = TemplateLibrary()
|
|
39
|
+
self.sampler = PromptSampler(config.prompt, library=lib)
|
|
40
|
+
self.ensemble = LLMEnsemble(config.ensemble, system_prompts=lib.system_prompts)
|
|
41
|
+
self.evaluator = Evaluator(config.evaluator)
|
|
42
|
+
self.database = ProgramDatabase(config.database)
|
|
43
|
+
seed = self.database.seed(initial_program)
|
|
44
|
+
self.database.add(seed, self.evaluator.execute(seed))
|
|
45
|
+
|
|
46
|
+
def run(self) -> RunResult:
|
|
47
|
+
best: Optional[Program] = None
|
|
48
|
+
cols = [
|
|
49
|
+
TextColumn("[bold cyan]evolve[/]"),
|
|
50
|
+
BarColumn(), MofNCompleteColumn(),
|
|
51
|
+
TextColumn("[yellow]{task.fields[stage]:>8}[/]"),
|
|
52
|
+
TextColumn("fit=[green]{task.fields[fit]:.3f}[/]"),
|
|
53
|
+
TextColumn("best=[magenta]{task.fields[best]:.3f}[/]"),
|
|
54
|
+
TimeElapsedColumn(), TimeRemainingColumn(),
|
|
55
|
+
]
|
|
56
|
+
with Progress(*cols, transient=False) as prog:
|
|
57
|
+
task = prog.add_task("run", total=self.config.iterations, stage="init", fit=0.0, best=0.0)
|
|
58
|
+
for _ in range(self.config.iterations):
|
|
59
|
+
prog.update(task, stage="sample")
|
|
60
|
+
with span("sample"):
|
|
61
|
+
parent, insp = self.database.sample()
|
|
62
|
+
prog.update(task, stage="prompt")
|
|
63
|
+
with span("prompt"):
|
|
64
|
+
prompt = self.sampler.build(parent, insp)
|
|
65
|
+
prog.update(task, stage="generate")
|
|
66
|
+
with span("generate"):
|
|
67
|
+
diff = self.ensemble.generate(prompt)
|
|
68
|
+
with span("apply"):
|
|
69
|
+
child = apply_diff(parent, diff)
|
|
70
|
+
child.island = parent.island
|
|
71
|
+
prog.update(task, stage="evaluate")
|
|
72
|
+
with span("evaluate"):
|
|
73
|
+
result = self.evaluator.execute(child)
|
|
74
|
+
self.database.add(child, result)
|
|
75
|
+
if best is None or child.fitness > best.fitness:
|
|
76
|
+
best = child
|
|
77
|
+
prog.update(task, advance=1, fit=child.fitness,
|
|
78
|
+
best=best.fitness if best else 0.0)
|
|
79
|
+
t = timings()
|
|
80
|
+
log.info("done. population=%d best=%.3f", len(self.database.by_id),
|
|
81
|
+
best.fitness if best else 0.0)
|
|
82
|
+
for name, s in t.items():
|
|
83
|
+
log.info("[cyan]%-9s[/] n=%-4d total=%6.2fs avg=%.3fs",
|
|
84
|
+
name, s["n"], s["total"], s["avg"])
|
|
85
|
+
return RunResult(best=best, iterations=self.config.iterations,
|
|
86
|
+
stats={"population": len(self.database.by_id), "timings": t})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from queue import PriorityQueue
|
|
2
|
+
|
|
3
|
+
from ..telemetry import log
|
|
4
|
+
from .result import EvaluationResult
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Evaluator:
|
|
8
|
+
def __init__(self, config):
|
|
9
|
+
self.config = config
|
|
10
|
+
self.pool: PriorityQueue = PriorityQueue()
|
|
11
|
+
|
|
12
|
+
def execute(self, child_program) -> EvaluationResult:
|
|
13
|
+
result = EvaluationResult()
|
|
14
|
+
for stage_fn, threshold in self.config.cascade:
|
|
15
|
+
name = getattr(stage_fn, "__name__", f"stage{len(result.scores)}")
|
|
16
|
+
try:
|
|
17
|
+
score = float(stage_fn(child_program))
|
|
18
|
+
except Exception:
|
|
19
|
+
log.exception("evaluator stage [yellow]%s[/] raised on program %s", name, child_program.id)
|
|
20
|
+
return result
|
|
21
|
+
result.scores[name] = score
|
|
22
|
+
if score < threshold:
|
|
23
|
+
return result
|
|
24
|
+
result.passed = True
|
|
25
|
+
result.behavior = tuple(max(0.0, min(1.0, v)) for v in result.scores.values())
|
|
26
|
+
return result
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass(kw_only=True)
|
|
5
|
+
class EvaluationResult:
|
|
6
|
+
scores: dict = field(default_factory=dict)
|
|
7
|
+
passed: bool = False
|
|
8
|
+
behavior: tuple = ()
|
|
9
|
+
extras: dict = field(default_factory=dict)
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def fitness(self) -> float:
|
|
13
|
+
return sum(self.scores.values()) / max(1, len(self.scores))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import List, Literal
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass(kw_only=True)
|
|
6
|
+
class ModelConfig:
|
|
7
|
+
name: str
|
|
8
|
+
temperature: float = 0.7
|
|
9
|
+
weight: float = 1.0
|
|
10
|
+
role: Literal["fast", "deep"] = "fast"
|
|
11
|
+
num_ctx: int = 4096
|
|
12
|
+
flash_attention: bool = True
|
|
13
|
+
options: dict = field(default_factory=dict)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(kw_only=True)
|
|
17
|
+
class EnsembleConfig:
|
|
18
|
+
models: List[ModelConfig] = field(default_factory=list)
|
|
19
|
+
host: str = "http://localhost:11434"
|
|
20
|
+
timeout: float = 600.0
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import random
|
|
2
|
+
from .ollama import OllamaLLM
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LLMEnsemble:
|
|
6
|
+
def __init__(self, config, *, system_prompts: dict | None = None):
|
|
7
|
+
self.config = config
|
|
8
|
+
sp = system_prompts or {}
|
|
9
|
+
self.llms = [OllamaLLM(m, host=config.host, timeout=config.timeout,
|
|
10
|
+
system_prompt=sp.get(m.name)) for m in config.models]
|
|
11
|
+
if not self.llms:
|
|
12
|
+
raise ValueError("EnsembleConfig.models is empty")
|
|
13
|
+
|
|
14
|
+
def select_model(self) -> OllamaLLM:
|
|
15
|
+
return random.choices(self.llms, weights=[m.cfg.weight for m in self.llms], k=1)[0]
|
|
16
|
+
|
|
17
|
+
def generate(self, prompt: str) -> str:
|
|
18
|
+
return self.select_model().generate(prompt)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from ollama import Client, ResponseError
|
|
2
|
+
|
|
3
|
+
from ..telemetry import log
|
|
4
|
+
from .base import BaseLLM
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class OllamaLLM(BaseLLM):
|
|
8
|
+
def __init__(self, model_config, *, host: str = "http://localhost:11434", timeout: float = 600.0, system_prompt: str | None = None):
|
|
9
|
+
self.cfg = model_config
|
|
10
|
+
self.system_prompt = system_prompt
|
|
11
|
+
self.client = Client(host=host, timeout=timeout)
|
|
12
|
+
self._ensure_model()
|
|
13
|
+
|
|
14
|
+
def _ensure_model(self):
|
|
15
|
+
try:
|
|
16
|
+
self.client.show(self.cfg.name)
|
|
17
|
+
except ResponseError:
|
|
18
|
+
log.info("[ollama] pulling [bold]%s[/]...", self.cfg.name)
|
|
19
|
+
self.client.pull(self.cfg.name)
|
|
20
|
+
|
|
21
|
+
def generate(self, prompt: str) -> str:
|
|
22
|
+
try:
|
|
23
|
+
return self._generate(prompt)
|
|
24
|
+
except Exception:
|
|
25
|
+
log.exception("ollama generate failed for model=%s", self.cfg.name)
|
|
26
|
+
raise
|
|
27
|
+
|
|
28
|
+
def _options(self) -> dict:
|
|
29
|
+
opts = {
|
|
30
|
+
"temperature": self.cfg.temperature,
|
|
31
|
+
"num_ctx": self.cfg.num_ctx,
|
|
32
|
+
"flash_attn": self.cfg.flash_attention,
|
|
33
|
+
}
|
|
34
|
+
opts.update(self.cfg.options)
|
|
35
|
+
return opts
|
|
36
|
+
|
|
37
|
+
def _generate(self, prompt: str) -> str:
|
|
38
|
+
resp = self.client.generate(
|
|
39
|
+
model=self.cfg.name,
|
|
40
|
+
prompt=prompt,
|
|
41
|
+
system=self.system_prompt,
|
|
42
|
+
options=self._options(),
|
|
43
|
+
stream=False,
|
|
44
|
+
)
|
|
45
|
+
return resp.response
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import heapq
|
|
2
|
+
import random
|
|
3
|
+
from collections import deque
|
|
4
|
+
from .program import Program
|
|
5
|
+
from .embedder import CodeEmbedder
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ProgramDatabase:
|
|
9
|
+
def __init__(self, config):
|
|
10
|
+
self.config = config
|
|
11
|
+
self.islands: list[dict] = [dict() for _ in range(config.num_islands)]
|
|
12
|
+
self.by_id: dict[int, Program] = {}
|
|
13
|
+
self.heap: list[tuple[float, int]] = []
|
|
14
|
+
self.migrations = [deque(maxlen=config.migration_size) for _ in range(config.num_islands)]
|
|
15
|
+
self.embedder = CodeEmbedder()
|
|
16
|
+
self.step = 0
|
|
17
|
+
|
|
18
|
+
def _cell(self, behavior):
|
|
19
|
+
if not behavior:
|
|
20
|
+
return (0,)
|
|
21
|
+
return tuple(min(self.config.cell_bins - 1, int(b * self.config.cell_bins)) for b in behavior)
|
|
22
|
+
|
|
23
|
+
def seed(self, code: str, *, island: int = 0) -> Program:
|
|
24
|
+
p = Program(code=code, island=island)
|
|
25
|
+
self.by_id[p.id] = p
|
|
26
|
+
self.islands[island][("seed", p.id)] = p
|
|
27
|
+
heapq.heappush(self.heap, (0.0, p.id))
|
|
28
|
+
self.embedder.add(self.embedder.embed(p), p.id)
|
|
29
|
+
return p
|
|
30
|
+
|
|
31
|
+
def add(self, child_program: Program, results):
|
|
32
|
+
child_program.fitness = results.fitness
|
|
33
|
+
child_program.behavior = results.behavior
|
|
34
|
+
cell = self._cell(results.behavior)
|
|
35
|
+
grid = self.islands[child_program.island % self.config.num_islands]
|
|
36
|
+
if cell not in grid or grid[cell].fitness < child_program.fitness:
|
|
37
|
+
grid[cell] = child_program
|
|
38
|
+
self.by_id[child_program.id] = child_program
|
|
39
|
+
heapq.heappush(self.heap, (-child_program.fitness, child_program.id))
|
|
40
|
+
self.embedder.add(self.embedder.embed(child_program), child_program.id)
|
|
41
|
+
self.step += 1
|
|
42
|
+
if self.step % self.config.migration_every == 0:
|
|
43
|
+
self._migrate()
|
|
44
|
+
|
|
45
|
+
def _migrate(self):
|
|
46
|
+
n = self.config.num_islands
|
|
47
|
+
for i, grid in enumerate(self.islands):
|
|
48
|
+
if grid:
|
|
49
|
+
self.migrations[(i + 1) % n].append(max(grid.values(), key=lambda p: p.fitness))
|
|
50
|
+
|
|
51
|
+
def sample(self):
|
|
52
|
+
top = heapq.nsmallest(self.config.top_k, self.heap)
|
|
53
|
+
live = [(f, pid) for f, pid in top if pid in self.by_id] or [(0.0, next(iter(self.by_id)))]
|
|
54
|
+
parent = self.by_id[random.choice(live)[1]]
|
|
55
|
+
ids = self.embedder.nearest(self.embedder.embed(parent), k=self.config.num_inspirations + 1)
|
|
56
|
+
insp = [self.by_id[i] for i in ids if i != parent.id and i in self.by_id][: self.config.num_inspirations]
|
|
57
|
+
return parent, insp
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import re
|
|
3
|
+
from collections import Counter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CodeEmbedder:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.index: list[tuple[Counter, int]] = []
|
|
9
|
+
|
|
10
|
+
def embed(self, program) -> Counter:
|
|
11
|
+
return Counter(re.findall(r"\w+", program.code))
|
|
12
|
+
|
|
13
|
+
def add(self, vector: Counter, program_id: int):
|
|
14
|
+
self.index.append((vector, program_id))
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def _cos(a: Counter, b: Counter) -> float:
|
|
18
|
+
num = sum(a[t] * b[t] for t in set(a) & set(b))
|
|
19
|
+
na = math.sqrt(sum(v * v for v in a.values()))
|
|
20
|
+
nb = math.sqrt(sum(v * v for v in b.values()))
|
|
21
|
+
return num / (na * nb) if na and nb else 0.0
|
|
22
|
+
|
|
23
|
+
def nearest(self, vector: Counter, *, k: int) -> list[int]:
|
|
24
|
+
scored = sorted(((self._cos(vector, v), pid) for v, pid in self.index), reverse=True)
|
|
25
|
+
return [pid for _, pid in scored[:k]]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Optional, Tuple
|
|
4
|
+
|
|
5
|
+
_ids = itertools.count()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Program:
|
|
10
|
+
code: str
|
|
11
|
+
id: int = field(default_factory=lambda: next(_ids), kw_only=True)
|
|
12
|
+
parent_id: Optional[int] = field(default=None, kw_only=True)
|
|
13
|
+
fitness: float = field(default=0.0, kw_only=True)
|
|
14
|
+
behavior: Tuple = field(default=(), kw_only=True)
|
|
15
|
+
island: int = field(default=0, kw_only=True)
|
|
16
|
+
generation: int = field(default=0, kw_only=True)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .template_library import TemplateLibrary
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PromptSampler:
|
|
5
|
+
def __init__(self, config, *, library: TemplateLibrary | None = None):
|
|
6
|
+
self.config = config
|
|
7
|
+
self.library = library or TemplateLibrary()
|
|
8
|
+
self.library.system_prompts.update(config.system_prompts)
|
|
9
|
+
|
|
10
|
+
def build(self, parent_program, inspirations):
|
|
11
|
+
insp = "\n---\n".join(p.code for p in inspirations) or "(none)"
|
|
12
|
+
return self.library.render(self.config.template, parent=parent_program.code, inspirations=insp)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
DEFAULT = """You are evolving a Python program. Improve the parent program.
|
|
4
|
+
|
|
5
|
+
# Parent
|
|
6
|
+
{parent}
|
|
7
|
+
|
|
8
|
+
# Inspirations
|
|
9
|
+
{inspirations}
|
|
10
|
+
|
|
11
|
+
Return ONLY the full replacement Python code (no prose, no fences)."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TemplateLibrary:
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.templates = {"default": DEFAULT}
|
|
17
|
+
self.system_prompts: dict[str, str] = {}
|
|
18
|
+
|
|
19
|
+
def load(self, path):
|
|
20
|
+
for f in Path(path).glob("*.txt"):
|
|
21
|
+
self.templates[f.stem] = f.read_text(encoding="utf-8")
|
|
22
|
+
|
|
23
|
+
def get(self, name): return self.templates[name]
|
|
24
|
+
|
|
25
|
+
def render(self, name, **vars): return self.templates[name].format(**vars)
|
fastevolve/telemetry.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from contextlib import contextmanager
|
|
6
|
+
|
|
7
|
+
from rich.logging import RichHandler
|
|
8
|
+
|
|
9
|
+
log = logging.getLogger("fastevolve")
|
|
10
|
+
_TIMINGS: dict[str, list[float]] = defaultdict(list)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def setup() -> None:
|
|
14
|
+
if logging.getLogger().handlers:
|
|
15
|
+
return
|
|
16
|
+
level = logging.WARNING if os.getenv("FASTEVOLVE_QUIET") else logging.INFO
|
|
17
|
+
logging.basicConfig(
|
|
18
|
+
level=level, format="%(message)s", datefmt="[%X]",
|
|
19
|
+
handlers=[RichHandler(rich_tracebacks=True, show_path=False, markup=True)],
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@contextmanager
|
|
24
|
+
def span(name: str):
|
|
25
|
+
t = time.perf_counter()
|
|
26
|
+
try:
|
|
27
|
+
yield
|
|
28
|
+
except Exception:
|
|
29
|
+
log.exception("[red]%s failed after %.2fs[/]", name, time.perf_counter() - t)
|
|
30
|
+
raise
|
|
31
|
+
finally:
|
|
32
|
+
_TIMINGS[name].append(time.perf_counter() - t)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def timings() -> dict[str, dict[str, float]]:
|
|
36
|
+
return {k: {"n": len(v), "total": sum(v), "avg": sum(v) / len(v)} for k, v in _TIMINGS.items()}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastevolve
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Minimal open-source AlphaEvolve: LLM-driven program evolution with MAP-Elites islands, cascade evaluation, and a local Ollama ensemble.
|
|
5
|
+
Project-URL: Homepage, https://github.com/tiagomonteiro/alpha_evolve_open_souce_version
|
|
6
|
+
Project-URL: Issues, https://github.com/tiagomonteiro/alpha_evolve_open_souce_version/issues
|
|
7
|
+
Author-email: Tiago Monteiro <monteiro.t@northeastern.edu>
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: alphaevolve,evolutionary-search,llm,map-elites,ollama,program-synthesis
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Requires-Dist: ollama>=0.4.0
|
|
17
|
+
Requires-Dist: rich>=13.7
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
20
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# fastevolve
|
|
24
|
+
|
|
25
|
+
Minimal open-source AlphaEvolve: LLM-driven program evolution with MAP-Elites islands, cascade evaluation, and a local Ollama ensemble.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv sync
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Run the demo
|
|
34
|
+
|
|
35
|
+
Start Ollama and pull the model first:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ollama serve
|
|
39
|
+
ollama pull gemma3:e4b
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv run python main.py
|
|
46
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
fastevolve/__init__.py,sha256=EPOCJKvRBM-PnX-m7sdDH1nQgTqyYPtdScoJXD4ZoqM,355
|
|
2
|
+
fastevolve/config.py,sha256=fqq7HZM_Nh1or1MR-ajJpJY_FQqiqnFGjJfNhunilBQ,575
|
|
3
|
+
fastevolve/controller.py,sha256=Z6qXAffclGTicUqsxrGb2rY5rzTjCKaU3AqvYE9UecE,3653
|
|
4
|
+
fastevolve/telemetry.py,sha256=EpThR2zHUxqdJ835v-JjxkY2Xas1G80SvsaM4bMQVrw,997
|
|
5
|
+
fastevolve/evaluator/__init__.py,sha256=IvelEVdP8TDKu1vc7H35R24H2hRXfgua94HxEH-NaJ0,170
|
|
6
|
+
fastevolve/evaluator/config.py,sha256=QBeOeIb8nuBoLmc6LXh3leIbnb99drF_imZPKw7fDaU,255
|
|
7
|
+
fastevolve/evaluator/evaluator.py,sha256=xL1B8GIBQwHDpNhyKWSagcS1uAXo7mH8FBja45K3SeA,941
|
|
8
|
+
fastevolve/evaluator/result.py,sha256=R416-7j2x0rPhdFKte6qSe6D1POCI9vTsw7qSJPb65c,351
|
|
9
|
+
fastevolve/llm_ensemble/__init__.py,sha256=-1CQiNjkp45ni5F2eVO-EXBxRiB11ktpI2xqzOyYL00,222
|
|
10
|
+
fastevolve/llm_ensemble/base.py,sha256=958WidTEGAx6eDx3rAY3Gqp1qKj6K2NINlTUKPmtWWI,126
|
|
11
|
+
fastevolve/llm_ensemble/config.py,sha256=8BazEWwbmZguxnYT6jC3TfLoPLdk7zqNwqUZGfYQsm0,512
|
|
12
|
+
fastevolve/llm_ensemble/ensemble.py,sha256=UhLMa_08SObEMYVkEUFF5v3d-uJJZ3Sj54NJtmgvsDE,680
|
|
13
|
+
fastevolve/llm_ensemble/ollama.py,sha256=PKfpfnwN8ebsiMNUYQDHqTEnwSWcYEuoRdaimCxqMSk,1422
|
|
14
|
+
fastevolve/program_database/__init__.py,sha256=3Pgkj5AssBXy7iOMvpxKUViP1K5oWdxl09LjcvqQdyg,213
|
|
15
|
+
fastevolve/program_database/config.py,sha256=v8bhchs8BbDoa6LE67Vsc4MmyI_5ktfGl3fO6udcquQ,240
|
|
16
|
+
fastevolve/program_database/database.py,sha256=Py_o9yuSGSGTZBOT74SmoB5w6NyrG9sSrgoPTJRUfsU,2438
|
|
17
|
+
fastevolve/program_database/embedder.py,sha256=uQGTNzNw8-npumjLVNEKxjI4sFmWVmkEjN2CiXAry6w,842
|
|
18
|
+
fastevolve/program_database/program.py,sha256=KJYSmu84LmIbXXjB0p1KENU-0fFA7iWXynXpSx_T47g,506
|
|
19
|
+
fastevolve/prompt_sampler/__init__.py,sha256=-YtYT7Rc7GwT0uTxNKwEte3XhaWwc-kZagGYhrCWGrw,178
|
|
20
|
+
fastevolve/prompt_sampler/config.py,sha256=6PRqLdzzTDcP7rkWxk4UYvP0tGI9xQdntpYAJWTNM1k,232
|
|
21
|
+
fastevolve/prompt_sampler/sampler.py,sha256=rQQrsQE_4Ba_zSmEI18hH-gJon0OM2JooPHuyrVGmfc,519
|
|
22
|
+
fastevolve/prompt_sampler/template_library.py,sha256=EvPyZlK8LURQ9qngMQBYdBl43WWL0PN3PHhSduYy_fM,639
|
|
23
|
+
fastevolve-0.2.0.dist-info/METADATA,sha256=UoDHGoR7ACe7FF-l1yVmDaTgEmbW2Wg9vLcWNSdgyvY,1354
|
|
24
|
+
fastevolve-0.2.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
25
|
+
fastevolve-0.2.0.dist-info/entry_points.txt,sha256=IpEsShMkKymR93wiyRle9nSCY03yiQPWshXWUsSE-pY,46
|
|
26
|
+
fastevolve-0.2.0.dist-info/RECORD,,
|