Source code for pytwinnet.optimization.ga
from __future__ import annotations
from dataclasses import dataclass
from typing import Dict, List, Any
import random
from ..core.digital_twin import DigitalTwin
from .objective import Objective
[docs]
@dataclass
class ChannelAllocationGA:
"""Toy GA for assigning channels (ints) to tx nodes to maximize objective."""
channels: List[int]
population: int = 30
generations: int = 20
mutation_p: float = 0.1
seed: int = 0
[docs]
def optimize(self, twin: DigitalTwin, node_ids: List[str], objective: Objective) -> Dict[str, Any]:
rng = random.Random(self.seed)
def random_ind():
return {nid: rng.choice(self.channels) for nid in node_ids}
def fitness(ind):
sim = twin.snapshot()
for nid, ch in ind.items():
n = sim.network.get_node_by_id(nid)
if n:
n.metadata["channel"] = ch
return objective.evaluate(sim)
pop = [random_ind() for _ in range(self.population)]
scores = [fitness(ind) for ind in pop]
for _ in range(self.generations):
parents = []
for _ in range(self.population):
cand = rng.sample(range(self.population), k=min(3, self.population))
best_i = max(cand, key=lambda i: scores[i])
parents.append(pop[best_i])
children = []
for i in range(0, self.population, 2):
p1 = parents[i]
p2 = parents[min(i + 1, self.population - 1)]
keys = list(p1.keys())
cut = rng.randrange(1, len(keys)) if len(keys) > 1 else 1
c = {k: (p1[k] if j < cut else p2[k]) for j, k in enumerate(keys)}
children.append(c)
for c in children:
if rng.random() < self.mutation_p:
k = rng.choice(list(c.keys()))
c[k] = rng.choice(self.channels)
pop = children
scores = [fitness(ind) for ind in pop]
best_idx = max(range(self.population), key=lambda i: scores[i])
return {"best_allocation": pop[best_idx], "best_score": scores[best_idx]}