SurvivorSelection#
GA (saealib.GA) delegates choosing which individuals survive into the next generation from a selection pool to SurvivorSelection, a swappable operator.
To change the generational-replacement scheme, you only need to swap out this SurvivorSelection, not GA itself.
SurvivorSelection’s role#
SurvivorSelection requires only one method, select(ctx, pool, n_survivors) -> np.ndarray, to be implemented.
pool is a combined Population built by Algorithm — for example, the parent and offspring populations together — and it returns the indices of the n_survivors individuals that survive from it.
Whether it’s a \((\mu+\lambda)\) scheme (selecting from a pool of parents and offspring combined) or a \((\mu,\lambda)\) scheme (selecting from a pool of offspring only) is determined by how Algorithm builds pool — that is, what it includes.
SurvivorSelection’s interface itself doesn’t distinguish between the two schemes.
Built-in SurvivorSelections#
Class |
Parameters |
Characteristics |
|---|---|---|
|
|
Truncation selection: ranks via |
Setting randomize_ties=True shuffles individuals tied at the truncation boundary (where compare_population returns 0) before truncating.
With the default False, it’s a deterministic truncation that uses the order returned by rank_population as-is.
Because this tie-breaking consumes ctx.rng, note that using randomize_ties=True also affects the random state at checkpoint resumption.
TruncationSelection is @register()ed.
Implementing a custom SurvivorSelection#
If you need a custom generational-replacement scheme, subclass SurvivorSelection and implement only select().
The following example is a survivor selection that always keeps the single best individual, choosing the rest at random.
import numpy as np
from saealib import SurvivorSelection
class ElitistSurvivorSelection(SurvivorSelection):
"""Always keeps the single best individual, choosing the rest at random."""
def select(self, ctx, pool, n_survivors):
sorted_idx = ctx.comparator.rank_population(pool)
best = sorted_idx[:1]
rest_pool = sorted_idx[1:]
rest = ctx.rng.choice(rest_pool, size=n_survivors - 1, replace=False)
return np.concatenate([best, rest])
pool is freshly assembled by Algorithm on every call, so a custom select() should rank it via rank_population, not sort_population directly — see Comparator for why.
Schemes that don’t assume a ranking at all, such as tournament-style survivor selection or age-based replacement, can also be implemented within the same select() signature.