Files
gausse/tests/test_evolutionary.py
jze9 51464b2139 Островная эволюция: K популяций одним GPU-батчем + миграция по кольцу
GPU на маленьких батчах упирается в накладные на запуск ядер (1070: батч
4000 = 306/с, 32000 = 1211/с при 99МБ видеопамяти из 8ГБ). Вместо одной
гигантской популяции — 8 независимых островов по 4000: их поколения
оцениваются одним жирным батчем 32к, а изоляция островов + миграция лучших
по кольцу раз в 10 поколений дают разнообразие против скатывания в один
локальный оптимум. На более мощной карте достаточно поднять islands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:31:45 +05:00

96 lines
3.5 KiB
Python

import random
from gausse.components.database import ComponentDatabase
from gausse.optim.evolutionary import polish_best, run_evolution
from gausse.optim.search_space import SearchBounds, sample_genome
from gausse.storage.database import count_runs, fetch_runs, open_connection
DB = ComponentDatabase.load()
def test_run_evolution_writes_all_evaluations_and_returns_summary(tmp_path):
db_path = tmp_path / "runs.sqlite3"
bounds = SearchBounds(max_stages=2)
summary = run_evolution(
db_path,
n_generations=2,
population_size=6,
bounds=bounds,
n_workers=2,
seed=7,
polish=False,
)
assert summary["n_evaluated"] == 12 # 2 поколения x 6 особей
assert "best_fitness" in summary
conn = open_connection(db_path)
assert count_runs(conn) == 12
rows = fetch_runs(conn)
assert all(r.search_mode == "evolve" for r in rows)
conn.close()
def test_evolution_is_reproducible_given_same_seed(tmp_path):
bounds = SearchBounds(max_stages=2)
summary_a = run_evolution(
tmp_path / "a.sqlite3", n_generations=2, population_size=6, bounds=bounds,
n_workers=1, seed=99, polish=False,
)
summary_b = run_evolution(
tmp_path / "b.sqlite3", n_generations=2, population_size=6, bounds=bounds,
n_workers=1, seed=99, polish=False,
)
assert summary_a["best_fitness"] == summary_b["best_fitness"]
assert summary_a["best_efficiency"] == summary_b["best_efficiency"]
def test_polish_does_not_make_the_best_genome_worse():
rng = random.Random(15)
bounds = SearchBounds(max_stages=2)
# ищем реализуемый геном как стартовую точку доводки
genome = None
for _ in range(30):
candidate = sample_genome(DB, bounds, rng)
from gausse.optim.objective import evaluate
result = evaluate(candidate, DB, bounds)
if result.feasible:
genome = candidate
baseline_fitness = result.fitness
break
assert genome is not None, "не нашли реализуемый геном за 30 попыток"
_, polished_result = polish_best(genome, DB, bounds)
assert polished_result.fitness >= baseline_fitness - 1e-9
def test_run_evolution_gpu_batch_mode(tmp_path):
"""GPU-режим эволюции (батч-оценка поколения; здесь numpy-бэкенд):
пишет прогоны в ту же БД и находит реализуемые конфигурации."""
from gausse.storage.database import count_runs, fetch_runs, open_connection
db_path = tmp_path / "evo_gpu.sqlite3"
summary = run_evolution(
db_path, n_generations=3, population_size=40, seed=5, polish=False, use_gpu=True
)
assert summary["n_evaluated"] == 120
conn = open_connection(db_path)
assert count_runs(conn) >= 120
rows = fetch_runs(conn, limit=5)
assert all(r.search_mode.startswith("evolve-gpu") for r in rows)
def test_run_evolution_islands(tmp_path):
"""Островная модель: K популяций одним батчем + миграция по кольцу."""
from gausse.storage.database import count_runs, open_connection
db_path = tmp_path / "evo_isl.sqlite3"
summary = run_evolution(
db_path, n_generations=4, population_size=15, seed=7, polish=False,
use_gpu=True, islands=3, migrate_every=2,
)
assert summary["n_evaluated"] == 4 * 15 * 3
conn = open_connection(db_path)
assert count_runs(conn) == 4 * 15 * 3