Skip to content

One-Fifth Success Rule

A (1+λ) Gaussian evolution strategy on bm_sphere. σ grows when more than one fifth of the offspring improve the parent, and shrinks otherwise. See the Strategies reference.

from deap_er import Fitness, Toolbox, creator, tools

tools.rng.seed(1234)  # disables randomization

DIM = 10
LAMBDA = 8
SIGMA0 = 0.5
GENERATIONS = 80


def setup():
    creator.create_type("FitnessMin", Fitness, weights=(-1.0,))
    creator.create_type("Individual", list, fitness=creator.FitnessMin, sigma=SIGMA0)

    toolbox = Toolbox()
    toolbox.register("attr_float", tools.rng.gauss, 0.0, 1.0)
    toolbox.register(
        "individual",
        tools.init_repeat,
        creator.Individual,
        toolbox.attr_float,
        DIM,
    )
    toolbox.register("evaluate", tools.bm_sphere)
    return toolbox


def mutate(parent):
    child = creator.Individual(gene + parent.sigma * tools.rng.gauss(0.0, 1.0) for gene in parent)
    child.sigma = parent.sigma
    return child


def print_results(best_fit):
    if best_fit >= 1.0:
        raise RuntimeError("One-fifth ES failed to reach the seed-fixed floor.")
    print(f"\nBest sphere fitness: {best_fit:.6f}")


def run_generation(toolbox, parent, best):
    successes = 0
    challenger = parent
    parent_fit = parent.fitness.values[0]
    for _ in range(LAMBDA):
        child = mutate(parent)
        child.fitness.values = toolbox.evaluate(child)
        fit = child.fitness.values[0]
        if fit < best:
            best = fit
        if fit >= parent_fit:
            continue
        successes += 1
        if fit < challenger.fitness.values[0]:
            challenger = child
    if successes > 0:
        parent = challenger
    rate = successes / LAMBDA
    if rate > 0.2:
        parent.sigma *= 1.2
    elif rate < 0.2:
        parent.sigma /= 1.2
    return parent, best


def main():
    toolbox = setup()
    parent = toolbox.individual()
    parent.sigma = SIGMA0
    parent.fitness.values = toolbox.evaluate(parent)
    best = parent.fitness.values[0]

    for _ in range(GENERATIONS):
        parent, best = run_generation(toolbox, parent, best)

    print_results(best)


if __name__ == "__main__":
    main()