Random Search
Uniformly sample a parent and context from the full candidate history.
"""Random-search SelectionPolicy component — uniform-random parent and context.
This is the one component that differs from a greedy keep-all baseline. Instead of taking the rank-1
program as the parent and ranks 2..K+1 as context, every iteration draws the parent uniformly from the
full population and samples distinct inspirations uniformly from the rest. Fitness plays no role — that
is the point of the baseline.
"""
from __future__ import annotations
from ...components.selection import SelectionPolicy
from ...records import LoopContext, Selection
class RandomSelectionPolicy(SelectionPolicy):
"""Select a uniform-random parent and distinct random inspirations over the whole history.
The draws use the base policy's seeded :attr:`rng`, so a given ``seed`` and population history
reproduce the same choices, and the inherited ``state_dict`` checkpoints the RNG cursor so a resumed
run continues the same stream. On the lone-seed step (no other candidates yet) the seed is used as
its own inspiration, so the inspiration set is never empty.
"""
def __init__(self, seed: int = 0, num_inspirations: int = 4):
super().__init__(seed)
self.num_inspirations = max(0, int(num_inspirations))
def select(self, population, ctx: LoopContext | None = None) -> Selection:
members = population.all()
if not members:
raise RuntimeError("cannot select from an empty population")
parent = self.rng.choice(members)
candidates = [genome for genome in members if genome.id != parent.id]
count = min(self.num_inspirations, len(candidates))
inspirations = self.rng.sample(candidates, count)
if not inspirations:
inspirations = [parent]
return Selection(
parent=parent,
inspirations=inspirations,
pool=members,
details={
"selection_strategy": "uniform_random",
"selection_mode": "explore",
"requested_inspirations": self.num_inspirations,
},
)