diff --git a/src/multi_swarm/ga/initial.py b/src/multi_swarm/ga/initial.py new file mode 100644 index 0000000..3d550d9 --- /dev/null +++ b/src/multi_swarm/ga/initial.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +import random + +from ..genome.hypothesis import HypothesisAgentGenome, ModelTier +from ..genome.mutation import COGNITIVE_STYLES + +STYLE_PROMPTS: dict[str, str] = { + "physicist": ( + "Cerca leggi conservative, simmetrie, regimi di scala. " + "Pensa in termini di flussi e potenziali." + ), + "biologist": ( + "Cerca pattern adattivi, nicchie ecologiche, " + "predator-prey dynamics tra partecipanti del mercato." + ), + "historian": ( + "Cerca pattern ricorrenti su scale temporali multiple, " + "analogie con regimi storici, mean reversion strutturali." + ), + "meteorologist": ( + "Cerca regimi di volatilità che si autoalimentano, " + "transizioni di stato come fronti, persistenza locale." + ), + "ecologist": ( + "Cerca interazioni multi-asset, correlazioni cluster, " + "segnali di stress sistemico nelle dinamiche di flusso." + ), + "engineer": ( + "Cerca segnali con rapporto S/N favorevole, filtri causali, " + "robustezza a perturbazioni di calibrazione." + ), +} + + +def build_initial_population( + k: int, + model_tier: ModelTier, + rng: random.Random, + feature_pool: tuple[str, ...] = ("close", "high", "low", "volume"), +) -> list[HypothesisAgentGenome]: + """Costruisce una popolazione iniziale K varia per stile cognitivo + parametri.""" + population: list[HypothesisAgentGenome] = [] + for i in range(k): + style = COGNITIVE_STYLES[i % len(COGNITIVE_STYLES)] + n_features = rng.randint(1, len(feature_pool)) + feats = sorted(rng.sample(feature_pool, k=n_features)) + g = HypothesisAgentGenome( + system_prompt=STYLE_PROMPTS[style], + feature_access=feats, + temperature=round(rng.uniform(0.7, 1.2), 2), + top_p=0.95, + model_tier=model_tier, + lookback_window=rng.choice([100, 150, 200, 300]), + cognitive_style=style, + ) + # Seed per garantire id univoco se duplicato (raro ma possibile) + while any(g.id == p.id for p in population): + g = HypothesisAgentGenome( + system_prompt=g.system_prompt + f" [seed-{i}-{rng.randint(0, 1_000_000)}]", + feature_access=g.feature_access, + temperature=g.temperature, + top_p=g.top_p, + model_tier=g.model_tier, + lookback_window=g.lookback_window, + cognitive_style=g.cognitive_style, + ) + population.append(g) + return population diff --git a/tests/unit/test_ga_initial.py b/tests/unit/test_ga_initial.py new file mode 100644 index 0000000..00e11d8 --- /dev/null +++ b/tests/unit/test_ga_initial.py @@ -0,0 +1,27 @@ +import random + +from multi_swarm.ga.initial import build_initial_population +from multi_swarm.genome.hypothesis import ModelTier + + +def test_initial_population_size(): + pop = build_initial_population(k=20, model_tier=ModelTier.C, rng=random.Random(0)) + assert len(pop) == 20 + + +def test_initial_population_unique_ids(): + pop = build_initial_population(k=20, model_tier=ModelTier.C, rng=random.Random(0)) + ids = {g.id for g in pop} + assert len(ids) == 20 + + +def test_initial_population_covers_all_styles(): + pop = build_initial_population(k=12, model_tier=ModelTier.C, rng=random.Random(0)) + styles = {g.cognitive_style for g in pop} + assert len(styles) == 6 + + +def test_initial_population_generation_zero(): + pop = build_initial_population(k=20, model_tier=ModelTier.C, rng=random.Random(0)) + assert all(g.generation == 0 for g in pop) + assert all(g.parent_ids == [] for g in pop)