feat(genome): deterministic mutation operators (numeric + categorical)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-09 19:48:22 +02:00
parent 42a95a52b5
commit 80f4477f72
2 changed files with 138 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
from __future__ import annotations
import random
from typing import Any
from .hypothesis import HypothesisAgentGenome
FEATURE_POOL: tuple[str, ...] = ("open", "high", "low", "close", "volume")
COGNITIVE_STYLES: tuple[str, ...] = (
"physicist",
"biologist",
"historian",
"meteorologist",
"ecologist",
"engineer",
)
def _clone_with(g: HypothesisAgentGenome, **overrides: Any) -> HypothesisAgentGenome:
payload: dict[str, Any] = g.to_dict()
payload.update(overrides)
payload.pop("id", None)
payload["parent_ids"] = [*g.parent_ids, g.id]
payload["generation"] = g.generation + 1
return HypothesisAgentGenome.from_dict(payload)
def mutate_temperature(g: HypothesisAgentGenome, rng: random.Random) -> HypothesisAgentGenome:
delta = rng.choice([-0.1, 0.1])
new_t = max(0.6, min(1.3, g.temperature + delta))
return _clone_with(g, temperature=round(new_t, 4))
def mutate_lookback(g: HypothesisAgentGenome, rng: random.Random) -> HypothesisAgentGenome:
delta = rng.choice([-50, 50])
new_lb = max(50, min(500, g.lookback_window + delta))
return _clone_with(g, lookback_window=new_lb)
def mutate_feature_access(g: HypothesisAgentGenome, rng: random.Random) -> HypothesisAgentGenome:
current = set(g.feature_access)
if len(current) == len(FEATURE_POOL):
op = "remove"
elif len(current) <= 1:
op = "add"
else:
op = rng.choice(["add", "remove"])
if op == "add":
candidates = [f for f in FEATURE_POOL if f not in current]
choice = rng.choice(candidates)
new_set = current | {choice}
else:
choice = rng.choice(sorted(current))
new_set = current - {choice}
return _clone_with(g, feature_access=sorted(new_set))
def mutate_cognitive_style(g: HypothesisAgentGenome, rng: random.Random) -> HypothesisAgentGenome:
candidates = [s for s in COGNITIVE_STYLES if s != g.cognitive_style]
new_style = rng.choice(candidates)
return _clone_with(g, cognitive_style=new_style)
MUTATION_OPS = (
mutate_temperature,
mutate_lookback,
mutate_feature_access,
mutate_cognitive_style,
)
def random_mutate(g: HypothesisAgentGenome, rng: random.Random) -> HypothesisAgentGenome:
op = rng.choice(MUTATION_OPS)
return op(g, rng)