fastevolve 0.3.2__tar.gz → 0.3.4__tar.gz

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.
Files changed (34) hide show
  1. {fastevolve-0.3.2 → fastevolve-0.3.4}/PKG-INFO +13 -16
  2. {fastevolve-0.3.2 → fastevolve-0.3.4}/README.md +12 -15
  3. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/llm_ensemble/__init__.py +2 -2
  4. fastevolve-0.3.4/fastevolve/llm_ensemble/ollama.py +129 -0
  5. {fastevolve-0.3.2 → fastevolve-0.3.4}/pyproject.toml +1 -1
  6. {fastevolve-0.3.2 → fastevolve-0.3.4}/uv.lock +1 -1
  7. fastevolve-0.3.2/fastevolve/llm_ensemble/ollama.py +0 -45
  8. {fastevolve-0.3.2 → fastevolve-0.3.4}/.claude/settings.local.json +0 -0
  9. {fastevolve-0.3.2 → fastevolve-0.3.4}/.gitignore +0 -0
  10. {fastevolve-0.3.2 → fastevolve-0.3.4}/.python-version +0 -0
  11. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/__init__.py +0 -0
  12. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/checkpoint.py +0 -0
  13. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/config.py +0 -0
  14. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/controller.py +0 -0
  15. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/evaluator/__init__.py +0 -0
  16. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/evaluator/config.py +0 -0
  17. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/evaluator/evaluator.py +0 -0
  18. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/evaluator/result.py +0 -0
  19. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/llm_ensemble/anthropic_llm.py +0 -0
  20. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/llm_ensemble/base.py +0 -0
  21. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/llm_ensemble/config.py +0 -0
  22. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/llm_ensemble/ensemble.py +0 -0
  23. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/llm_ensemble/openai_llm.py +0 -0
  24. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/program_database/__init__.py +0 -0
  25. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/program_database/config.py +0 -0
  26. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/program_database/database.py +0 -0
  27. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/program_database/embedder.py +0 -0
  28. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/program_database/program.py +0 -0
  29. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/prompt_sampler/__init__.py +0 -0
  30. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/prompt_sampler/config.py +0 -0
  31. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/prompt_sampler/sampler.py +0 -0
  32. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/prompt_sampler/template_library.py +0 -0
  33. {fastevolve-0.3.2 → fastevolve-0.3.4}/fastevolve/telemetry.py +0 -0
  34. {fastevolve-0.3.2 → fastevolve-0.3.4}/main.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastevolve
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Minimal open-source AlphaEvolve: LLM-driven program evolution with MAP-Elites islands, cascade evaluation, and a local Ollama ensemble.
5
5
  Project-URL: Homepage, https://github.com/tiagomonteiro0715/fastevolve
6
6
  Project-URL: Repository, https://github.com/tiagomonteiro0715/fastevolve
@@ -99,7 +99,7 @@ cfg = Config()
99
99
  cfg.iterations = 20
100
100
  cfg.checkpoint_path = "run.log" # optional — resume if killed mid-run
101
101
  cfg.ensemble.models = [
102
- ModelConfig(name="gemma3:e4b", provider="ollama", temperature=0.7, weight=1.0, role="fast"),
102
+ ModelConfig(name="gemma4:e4b", provider="ollama", temperature=0.7, weight=1.0, role="fast"),
103
103
  ]
104
104
  cfg.evaluator.cascade = [(correctness, 0.0)]
105
105
 
@@ -179,22 +179,19 @@ print(result.best.code)
179
179
 
180
180
  ### Google Colab (with Ollama)
181
181
 
182
- Ollama can run on Colab if you install it, start the daemon in the background, and pull a small model. Use a GPU runtime (`Runtime Change runtime type T4 GPU`) for any model bigger than ~1B parameters.
182
+ Ollama can run on Colab if you install it, start the daemon in the background, and pull a model. Tested working on the free CPU runtime with a tiny model (`qwen2.5:0.5b`).
183
+
184
+ **On Colab Pro / Pro+**: switch to an A100 or L4 GPU runtime (`Runtime → Change runtime type → A100 GPU`) and swap the model for something bigger — `qwen2.5-coder:7b`, `llama3.1:8b`, or `gemma2:9b` all fit comfortably and produce dramatically better evolution candidates than `0.5b`. Pro+'s longer sessions (24 h) and background execution also mean you can leave a 1000-iteration run going overnight without keeping the tab open.
183
185
 
184
186
  ```python
185
- # 1. Install ollama and fastevolve
187
+ # 1. Install ollama (zstd is required by the install script) and fastevolve via uv
188
+ !apt-get -qq install -y zstd
186
189
  !curl -fsSL https://ollama.com/install.sh | sh
187
- !pip install -q fastevolve
188
-
189
- # 2. Start the ollama daemon in the background
190
- import subprocess, time
191
- subprocess.Popen(["ollama", "serve"])
192
- time.sleep(5) # give it a moment to bind to port 11434
193
-
194
- # 3. Pull a small model (qwen2.5:0.5b is ~400 MB and fits the free CPU runtime)
195
- !ollama pull qwen2.5:0.5b
190
+ !pip install uv
191
+ !uv pip install -q fastevolve
196
192
 
197
- # 4. Run fastevolve as usual
193
+ # 2. Run fastevolve it starts the ollama daemon automatically with GPU-aware
194
+ # optimizations (flash attention, q8_0 KV cache, parallel decoding) when a GPU is detected.
198
195
  from fastevolve import Config, Controller
199
196
  from fastevolve.llm_ensemble import ModelConfig
200
197
 
@@ -228,7 +225,7 @@ Start Ollama and pull the model first:
228
225
 
229
226
  ```bash
230
227
  ollama serve
231
- ollama pull gemma3:e4b
228
+ ollama pull gemma4:e4b
232
229
  ```
233
230
 
234
231
  Then:
@@ -254,7 +251,7 @@ Then pick a `provider` per model in your config. You can freely mix providers in
254
251
  from fastevolve.llm_ensemble import ModelConfig
255
252
 
256
253
  cfg.ensemble.models = [
257
- ModelConfig(name="gemma3:e4b", provider="ollama", temperature=0.6, weight=1.0, role="fast"),
254
+ ModelConfig(name="gemma4:e4b", provider="ollama", temperature=0.6, weight=1.0, role="fast"),
258
255
  ModelConfig(name="gpt-4o-mini", provider="openai", temperature=0.6, weight=1.0, role="fast"),
259
256
  ModelConfig(name="claude-opus-4-7", provider="anthropic", temperature=0.7, weight=1.0,
260
257
  role="deep", options={"max_tokens": 4096}),
@@ -69,7 +69,7 @@ cfg = Config()
69
69
  cfg.iterations = 20
70
70
  cfg.checkpoint_path = "run.log" # optional — resume if killed mid-run
71
71
  cfg.ensemble.models = [
72
- ModelConfig(name="gemma3:e4b", provider="ollama", temperature=0.7, weight=1.0, role="fast"),
72
+ ModelConfig(name="gemma4:e4b", provider="ollama", temperature=0.7, weight=1.0, role="fast"),
73
73
  ]
74
74
  cfg.evaluator.cascade = [(correctness, 0.0)]
75
75
 
@@ -149,22 +149,19 @@ print(result.best.code)
149
149
 
150
150
  ### Google Colab (with Ollama)
151
151
 
152
- Ollama can run on Colab if you install it, start the daemon in the background, and pull a small model. Use a GPU runtime (`Runtime Change runtime type T4 GPU`) for any model bigger than ~1B parameters.
152
+ Ollama can run on Colab if you install it, start the daemon in the background, and pull a model. Tested working on the free CPU runtime with a tiny model (`qwen2.5:0.5b`).
153
+
154
+ **On Colab Pro / Pro+**: switch to an A100 or L4 GPU runtime (`Runtime → Change runtime type → A100 GPU`) and swap the model for something bigger — `qwen2.5-coder:7b`, `llama3.1:8b`, or `gemma2:9b` all fit comfortably and produce dramatically better evolution candidates than `0.5b`. Pro+'s longer sessions (24 h) and background execution also mean you can leave a 1000-iteration run going overnight without keeping the tab open.
153
155
 
154
156
  ```python
155
- # 1. Install ollama and fastevolve
157
+ # 1. Install ollama (zstd is required by the install script) and fastevolve via uv
158
+ !apt-get -qq install -y zstd
156
159
  !curl -fsSL https://ollama.com/install.sh | sh
157
- !pip install -q fastevolve
158
-
159
- # 2. Start the ollama daemon in the background
160
- import subprocess, time
161
- subprocess.Popen(["ollama", "serve"])
162
- time.sleep(5) # give it a moment to bind to port 11434
163
-
164
- # 3. Pull a small model (qwen2.5:0.5b is ~400 MB and fits the free CPU runtime)
165
- !ollama pull qwen2.5:0.5b
160
+ !pip install uv
161
+ !uv pip install -q fastevolve
166
162
 
167
- # 4. Run fastevolve as usual
163
+ # 2. Run fastevolve it starts the ollama daemon automatically with GPU-aware
164
+ # optimizations (flash attention, q8_0 KV cache, parallel decoding) when a GPU is detected.
168
165
  from fastevolve import Config, Controller
169
166
  from fastevolve.llm_ensemble import ModelConfig
170
167
 
@@ -198,7 +195,7 @@ Start Ollama and pull the model first:
198
195
 
199
196
  ```bash
200
197
  ollama serve
201
- ollama pull gemma3:e4b
198
+ ollama pull gemma4:e4b
202
199
  ```
203
200
 
204
201
  Then:
@@ -224,7 +221,7 @@ Then pick a `provider` per model in your config. You can freely mix providers in
224
221
  from fastevolve.llm_ensemble import ModelConfig
225
222
 
226
223
  cfg.ensemble.models = [
227
- ModelConfig(name="gemma3:e4b", provider="ollama", temperature=0.6, weight=1.0, role="fast"),
224
+ ModelConfig(name="gemma4:e4b", provider="ollama", temperature=0.6, weight=1.0, role="fast"),
228
225
  ModelConfig(name="gpt-4o-mini", provider="openai", temperature=0.6, weight=1.0, role="fast"),
229
226
  ModelConfig(name="claude-opus-4-7", provider="anthropic", temperature=0.7, weight=1.0,
230
227
  role="deep", options={"max_tokens": 4096}),
@@ -1,6 +1,6 @@
1
1
  from .config import ModelConfig, EnsembleConfig
2
2
  from .base import BaseLLM
3
- from .ollama import OllamaLLM
3
+ from .ollama import OllamaLLM, start_ollama
4
4
  from .ensemble import LLMEnsemble
5
5
 
6
- __all__ = ["ModelConfig", "EnsembleConfig", "BaseLLM", "OllamaLLM", "LLMEnsemble"]
6
+ __all__ = ["ModelConfig", "EnsembleConfig", "BaseLLM", "OllamaLLM", "LLMEnsemble", "start_ollama"]
@@ -0,0 +1,129 @@
1
+ import os
2
+ import platform
3
+ import shutil
4
+ import subprocess
5
+ import time
6
+ from functools import cache
7
+
8
+ from ollama import Client, ResponseError
9
+
10
+ from ..telemetry import log
11
+ from .base import BaseLLM
12
+
13
+
14
+ @cache
15
+ def _gpu_available() -> bool:
16
+ if not shutil.which("nvidia-smi"):
17
+ return False
18
+ try:
19
+ return subprocess.run(["nvidia-smi"], capture_output=True, timeout=2).returncode == 0
20
+ except Exception:
21
+ return False
22
+
23
+
24
+ @cache
25
+ def _hardware_info() -> str:
26
+ if _gpu_available():
27
+ try:
28
+ r = subprocess.run(
29
+ ["nvidia-smi", "--query-gpu=name", "--format=csv,noheader"],
30
+ capture_output=True, text=True, timeout=2,
31
+ )
32
+ gpus = [g.strip() for g in r.stdout.strip().splitlines() if g.strip()]
33
+ if gpus:
34
+ tag = f"{gpus[0]}" + (f" ×{len(gpus)}" if len(gpus) > 1 else "")
35
+ return f"GPU: [bold green]{tag}[/]"
36
+ except Exception:
37
+ pass
38
+ return "GPU: [bold green]detected[/]"
39
+ cpu = platform.processor() or ""
40
+ if not cpu and os.path.exists("/proc/cpuinfo"):
41
+ try:
42
+ with open("/proc/cpuinfo") as f:
43
+ for line in f:
44
+ if line.startswith("model name"):
45
+ cpu = line.split(":", 1)[1].strip()
46
+ break
47
+ except Exception:
48
+ pass
49
+ return f"CPU: [bold yellow]{cpu or 'unknown'}[/] ({os.cpu_count() or '?'} cores)"
50
+
51
+
52
+ def start_ollama(host: str = "127.0.0.1:11434", *, wait: float = 5.0) -> None:
53
+ """Start an ollama daemon with GPU-aware optimizations. No-op if one is already running."""
54
+ for prefix in ("http://", "https://"):
55
+ if host.startswith(prefix):
56
+ host = host[len(prefix):]
57
+ try:
58
+ Client(host=f"http://{host}").list()
59
+ log.info("[ollama] server already running on %s", host)
60
+ return
61
+ except Exception:
62
+ pass
63
+
64
+ env = os.environ.copy()
65
+ env["OLLAMA_HOST"] = host
66
+ if _gpu_available():
67
+ env.setdefault("OLLAMA_FLASH_ATTENTION", "1")
68
+ env.setdefault("OLLAMA_KV_CACHE_TYPE", "q8_0")
69
+ env.setdefault("OLLAMA_NUM_PARALLEL", "4")
70
+ env.setdefault("OLLAMA_MAX_LOADED_MODELS", "2")
71
+ log.info("[ollama] starting server | %s | flash_attn, q8_0 kv-cache, parallel=4, max_loaded=2", _hardware_info())
72
+ else:
73
+ log.info("[ollama] starting server | %s", _hardware_info())
74
+
75
+ path = shutil.which("ollama") or "/usr/local/bin/ollama"
76
+ subprocess.Popen([path, "serve"], env=env,
77
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
78
+ time.sleep(wait)
79
+
80
+
81
+ class OllamaLLM(BaseLLM):
82
+ def __init__(self, model_config, *, host: str = "http://localhost:11434", timeout: float = 600.0, system_prompt: str | None = None):
83
+ self.cfg = model_config
84
+ self.system_prompt = system_prompt
85
+ self.client = Client(host=host, timeout=timeout)
86
+ self._gpu = _gpu_available()
87
+ try:
88
+ self.client.list()
89
+ except Exception:
90
+ start_ollama(host=host)
91
+ self.client = Client(host=host, timeout=timeout)
92
+ log.info("[ollama] %s | %s", self.cfg.name, _hardware_info())
93
+ self._ensure_model()
94
+
95
+ def _ensure_model(self):
96
+ try:
97
+ self.client.show(self.cfg.name)
98
+ except ResponseError:
99
+ log.info("[ollama] pulling [bold]%s[/]...", self.cfg.name)
100
+ self.client.pull(self.cfg.name)
101
+
102
+ def generate(self, prompt: str) -> str:
103
+ try:
104
+ return self._generate(prompt)
105
+ except Exception:
106
+ log.exception("ollama generate failed for model=%s", self.cfg.name)
107
+ raise
108
+
109
+ def _options(self) -> dict:
110
+ opts = {
111
+ "temperature": self.cfg.temperature,
112
+ "num_ctx": self.cfg.num_ctx,
113
+ "flash_attn": self.cfg.flash_attention and self._gpu,
114
+ "num_gpu": -1 if self._gpu else 0,
115
+ "num_thread": 0 if self._gpu else (os.cpu_count() or 4),
116
+ }
117
+ opts.update(self.cfg.options)
118
+ return opts
119
+
120
+ def _generate(self, prompt: str) -> str:
121
+ resp = self.client.generate(
122
+ model=self.cfg.name,
123
+ prompt=prompt,
124
+ system=self.system_prompt,
125
+ options=self._options(),
126
+ keep_alive="1h",
127
+ stream=False,
128
+ )
129
+ return resp.response
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fastevolve"
3
- version = "0.3.2"
3
+ version = "0.3.4"
4
4
  description = "Minimal open-source AlphaEvolve: LLM-driven program evolution with MAP-Elites islands, cascade evaluation, and a local Ollama ensemble."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -81,7 +81,7 @@ wheels = [
81
81
 
82
82
  [[package]]
83
83
  name = "fastevolve"
84
- version = "0.3.2"
84
+ version = "0.3.4"
85
85
  source = { editable = "." }
86
86
  dependencies = [
87
87
  { name = "ollama" },
@@ -1,45 +0,0 @@
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
File without changes
File without changes
File without changes