Skip to content

Estimation of Distribution

A custom generate / update pair that fits a diagonal Gaussian to the elite set and samples the next generation — the DEAP EDA recipe on bm_sphere. See Operators and Algorithms.

import numpy
from deap_er import Fitness, Toolbox, creator, tools

tools.rng.seed(1234)  # disables randomization

DIM = 10
POP = 40
SELECTED = 12
GENERATIONS = 25


class DiagonalGaussian:
    """Sample a diagonal Gaussian whose mean and variance track elites."""

    def __init__(self, dim):
        self.mean = numpy.zeros(dim)
        self.std = numpy.ones(dim)

    def generate(self, ind_init):
        samples = [tools.rng.gauss(self.mean[i], self.std[i]) for i in range(len(self.mean))]
        return [ind_init(samples)]

    def generate_pop(self, ind_init, count):
        return [self.generate(ind_init)[0] for _ in range(count)]

    def update(self, population):
        elite = tools.sel_best(population, SELECTED)
        matrix = numpy.asarray(elite, dtype=float)
        self.mean = matrix.mean(axis=0)
        self.std = numpy.maximum(matrix.std(axis=0), 1e-6)


def setup():
    creator.create_type("FitnessMin", Fitness, weights=(-1.0,))
    creator.create_type("Individual", list, fitness=creator.FitnessMin)
    strategy = DiagonalGaussian(DIM)
    toolbox = Toolbox()
    toolbox.register("evaluate", tools.bm_sphere)
    toolbox.register("generate", strategy.generate_pop, creator.Individual, POP)
    toolbox.register("update", strategy.update)
    stats = tools.Statistics(lambda ind: ind.fitness.values)
    stats.register("min", numpy.min)
    stats.register("avg", numpy.mean)
    return toolbox, stats


def print_results(best_ind):
    if best_ind.fitness.values[0] >= 1.0:
        raise RuntimeError("EDA failed to reach the seed-fixed floor.")
    print(f"\nBest sphere fitness: {best_ind.fitness.values[0]:.6f}")


def main():
    toolbox, stats = setup()
    hof = tools.HallOfFame(1)
    tools.ea_generate_update(
        toolbox,
        generations=GENERATIONS,
        hof=hof,
        stats=stats,
        verbose=True,
    )
    print_results(hof[0])


if __name__ == "__main__":
    main()