feat(ga): tournament selection + elitism
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
from ..genome.hypothesis import HypothesisAgentGenome
|
||||||
|
|
||||||
|
|
||||||
|
def tournament_select(
|
||||||
|
population: list[HypothesisAgentGenome],
|
||||||
|
fitnesses: dict[str, float],
|
||||||
|
k: int,
|
||||||
|
rng: random.Random,
|
||||||
|
) -> HypothesisAgentGenome:
|
||||||
|
"""Estrae k individui random e restituisce il migliore."""
|
||||||
|
if k < 1:
|
||||||
|
raise ValueError("k must be >= 1")
|
||||||
|
if not population:
|
||||||
|
raise ValueError("empty population")
|
||||||
|
candidates = rng.sample(population, k=min(k, len(population)))
|
||||||
|
return max(candidates, key=lambda g: fitnesses.get(g.id, 0.0))
|
||||||
|
|
||||||
|
|
||||||
|
def elite_select(
|
||||||
|
population: list[HypothesisAgentGenome],
|
||||||
|
fitnesses: dict[str, float],
|
||||||
|
k: int,
|
||||||
|
) -> list[HypothesisAgentGenome]:
|
||||||
|
"""Restituisce i k genomi con fitness più alta."""
|
||||||
|
sorted_pop = sorted(population, key=lambda g: fitnesses.get(g.id, 0.0), reverse=True)
|
||||||
|
return sorted_pop[:k]
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
from multi_swarm.ga.selection import elite_select, tournament_select
|
||||||
|
from multi_swarm.genome.hypothesis import HypothesisAgentGenome, ModelTier
|
||||||
|
|
||||||
|
|
||||||
|
def make(idx: int) -> HypothesisAgentGenome:
|
||||||
|
return HypothesisAgentGenome(
|
||||||
|
system_prompt=f"p-{idx}",
|
||||||
|
feature_access=["close"],
|
||||||
|
temperature=0.9,
|
||||||
|
top_p=0.95,
|
||||||
|
model_tier=ModelTier.C,
|
||||||
|
lookback_window=100,
|
||||||
|
cognitive_style="x",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_tournament_picks_best_in_sample() -> None:
|
||||||
|
population = [make(i) for i in range(10)]
|
||||||
|
fitnesses = {g.id: float(i) for i, g in enumerate(population)}
|
||||||
|
rng = random.Random(0)
|
||||||
|
winner = tournament_select(population, fitnesses, k=5, rng=rng)
|
||||||
|
assert isinstance(winner, HypothesisAgentGenome)
|
||||||
|
assert fitnesses[winner.id] >= 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_tournament_size_one_is_random() -> None:
|
||||||
|
population = [make(i) for i in range(10)]
|
||||||
|
fitnesses = {g.id: float(i) for i, g in enumerate(population)}
|
||||||
|
rng = random.Random(0)
|
||||||
|
picks = [tournament_select(population, fitnesses, k=1, rng=rng) for _ in range(50)]
|
||||||
|
distinct = {p.id for p in picks}
|
||||||
|
assert len(distinct) > 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_elite_select_returns_top_k() -> None:
|
||||||
|
population = [make(i) for i in range(10)]
|
||||||
|
fitnesses = {g.id: float(i) for i, g in enumerate(population)}
|
||||||
|
elites = elite_select(population, fitnesses, k=3)
|
||||||
|
elite_fitnesses = sorted([fitnesses[g.id] for g in elites], reverse=True)
|
||||||
|
assert elite_fitnesses == [9.0, 8.0, 7.0]
|
||||||
Reference in New Issue
Block a user