Skip to content

SPEA-II

ZDT1 with sel_spea_2. The density estimate uses the full distance row. See the Operators reference.

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

tools.rng.seed(1234)  # disables randomization

DIMENSIONS = 10
BOUND_LOW, BOUND_UP = 0.0, 1.0
SURVIVORS = 40
GENERATIONS = 30


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

    toolbox = Toolbox()
    toolbox.register("attr_float", tools.rng.uniform, BOUND_LOW, BOUND_UP)
    toolbox.register(
        "individual",
        tools.init_repeat,
        creator.Individual,
        toolbox.attr_float,
        DIMENSIONS,
    )
    toolbox.register("population", tools.init_repeat, list, toolbox.individual)
    toolbox.register(
        "mate",
        tools.cx_simulated_binary_bounded,
        eta=20.0,
        low=BOUND_LOW,
        up=BOUND_UP,
    )
    toolbox.register(
        "mutate",
        tools.mut_polynomial_bounded,
        eta=20.0,
        low=BOUND_LOW,
        up=BOUND_UP,
        mut_prob=1.0 / DIMENSIONS,
    )
    toolbox.register("evaluate", tools.bm_zdt_1)
    toolbox.register("select", tools.sel_spea_2)

    stats = tools.Statistics(lambda ind: ind.fitness.values)
    stats.register("min", numpy.min, axis=0)
    return toolbox, stats


def print_results(population):
    hv = tools.hypervolume(population, [11.0, 11.0])
    if hv < 80.0:
        raise RuntimeError(f"Hypervolume {hv:.4f} is below the seed-fixed floor.")
    print(f"\nSPEA-II hypervolume: {hv:.4f}")


def main():
    toolbox, stats = setup()
    pop = toolbox.population(size=SURVIVORS)
    hof = tools.ParetoFront()
    tools.ea_mu_plus_lambda(
        toolbox,
        pop,
        generations=GENERATIONS,
        offsprings=SURVIVORS,
        survivors=SURVIVORS,
        cx_prob=0.7,
        mut_prob=0.3,
        hof=hof,
        stats=stats,
        verbose=True,
    )
    print_results(pop)


if __name__ == "__main__":
    main()