Skip to content

Team and Archive

Semantic projection into a MAP-Elites archive, then sel_team_archive on the case-solve matrix from occupied cells. Archive add ranks on scalar fitness; team cover uses an explicit matrix=. See the columnar programs tutorial.

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

tools.rng.seed(1234)  # disables randomization

N_CASES = 6
IND_SIZE = 3
TARGET = (1, 0, 1, 0, 1, 0)


def case_values(individual):
    return tuple(0.0 if individual[i % IND_SIZE] == TARGET[i] else 1.0 for i in range(N_CASES))


def evaluate(individual):
    values = case_values(individual)
    return (sum(values) / len(values),)


def solved_count(matrix):
    solved = 0
    for col in range(matrix.shape[1]):
        if numpy.any(numpy.isclose(matrix[:, col], 0.0, atol=1e-12)):
            solved += 1
    return solved


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

    toolbox = Toolbox()
    toolbox.register("attr_bool", tools.rng.randint, 0, 1)
    toolbox.register(
        "individual",
        tools.init_repeat,
        creator.Individual,
        toolbox.attr_bool,
        IND_SIZE,
    )
    toolbox.register("population", tools.init_repeat, list, toolbox.individual)
    toolbox.register("mate", tools.cx_two_point)
    toolbox.register("mutate", tools.mut_flip_bit, mut_prob=0.3)
    toolbox.register("select", tools.sel_tournament, contestants=3)
    toolbox.register("evaluate", evaluate)
    toolbox.register("clone", tools.clone_individual)
    return toolbox


def fill_archive(population, archive):
    matrix = numpy.asarray([case_values(ind) for ind in population], dtype=numpy.float64)
    basis = numpy.eye(N_CASES, dtype=numpy.float64)
    descriptors = tools.semantic_project(matrix, basis, trust_matrix=True)
    for individual, descriptor in zip(population, descriptors, strict=True):
        ranker = tools.clone_individual(individual)
        ranker.fitness.values = evaluate(individual)
        archive.add(ranker, descriptor)


def print_results(population, archive, team):
    matrix = numpy.asarray([case_values(ind) for ind in population], dtype=numpy.float64)
    best_single = max(solved_count(matrix[idx : idx + 1]) for idx in range(matrix.shape[0]))
    team_rows = numpy.asarray([case_values(member) for member in team], dtype=numpy.float64)
    team_union = solved_count(team_rows)
    if team_union <= best_single:
        raise RuntimeError(
            f"Team coverage {team_union} did not beat the best single ({best_single})."
        )
    print(f"\nArchive elites: {len(archive)}")
    print(f"Best single solved {best_single} cases; team solved {team_union}.")


def main():
    toolbox = setup()
    archive = tools.UnstructuredArchive(N_CASES, min_distance=0.2, max_elites=12)
    pop = toolbox.population(size=24)
    tools.ea_simple(
        toolbox,
        pop,
        generations=8,
        cx_prob=0.5,
        mut_prob=0.3,
        verbose=True,
    )
    specialists = [creator.Individual([1, 0, 1]), creator.Individual([0, 1, 0])]
    for ind in specialists:
        ind.fitness.values = evaluate(ind)
        pop.append(ind)
    fill_archive(pop, archive)
    matrix = numpy.asarray([case_values(ind) for ind in archive], dtype=numpy.float64)
    team = tools.sel_team_archive(
        archive,
        2,
        matrix=matrix,
        trust_matrix=True,
    )
    print_results(pop, archive, team)


if __name__ == "__main__":
    main()