# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/00_algorithms.ipynb (unless otherwise specified).

__all__ = ['eaSimpleWithExtraLog', 'eaMuPlusLambdaWithExtraLog', 'eaMuCommaLambdaWithExtraLog']

# Cell
from deap import algorithms
from deap import base
from deap import creator
from deap import tools
from deap import algorithms
from functools import partial

def eaSimpleWithExtraLog(population, toolbox, cxpb, mutpb, ngen, stats=None,
             halloffame=None, elitism=False, verbose=__debug__):
    """This algorithm add extra log and elitism to deap eaSimple algorithm.

    **Parameters**

    - population:
        A list of individuals.
    - toolbox:
        A `~deap.base.Toolbox` that contains the evolution operators.
    - cxpb:
        The probability of mating two individuals.
    - mutpb:
        The probability of mutating an individual.
    - ngen:
        The number of generation.
    - stats:
         A `~deap.tools.Statistics` object that is updated inplace, optional.
    - halloffame:
        A `~deap.tools.HallOfFame` object that will contain the best individuals, optional.
    - elitism:
        Whether or not to keep the hall of fame in the offspring.
    - verbose:
        Whether or not to log the statistics.

    **Returns**

    - The final population
    - A `~deap.tools.Logbook` with the statistics of the evolution
    """
    logbook = tools.Logbook()
    logbook.header = ['gen', 'nevals'] + (stats.fields if stats else [])

    # Evaluate the individuals with an invalid fitness
    invalid_ind = [ind for ind in population if not ind.fitness.valid]
    fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
    for ind, fit in zip(invalid_ind, fitnesses):
        if len(fit)==2:
            # Monkey patch the attribute to ind.extra
            ind.fitness.values, ind.extra = fit
        else:
            ind.fitness.values = fit

    if halloffame is not None:
        halloffame.update(population)
    if elitism and halloffame is None:
        raise ValueError('elitism must used with halloffame.')
    if elitism:
        hof_size = len(halloffame.items)

    record = stats.compile(population) if stats else {}
    logbook.record(gen=0, nevals=len(invalid_ind), **record)
    if verbose:
        print(logbook.stream)

    # Begin the generational process
    for gen in range(1, ngen + 1):
        # Select the next generation individuals
        if elitism:
            offspring = toolbox.select(population, len(population) - hof_size)
        else:
            offspring = toolbox.select(population, len(population))

        # Vary the pool of individuals
        offspring = algorithms.varAnd(offspring, toolbox, cxpb, mutpb)

        # Evaluate the individuals with an invalid fitness
        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            if len(fit)==2:
                # Monkey patch the attribute to ind.extra
                ind.fitness.values, ind.extra = fit
            else:
                ind.fitness.values = fit

        # No need to recalculate fitness values for halloffame items.
        if elitism:
            offspring.extend(halloffame.items)

        # Update the hall of fame with the generated individuals
        if halloffame is not None:
            halloffame.update(offspring)

        # Replace the current population by the offspring
        population[:] = offspring

        # Append the current generation statistics to the logbook
        record = stats.compile(population) if stats else {}
        logbook.record(gen=gen, nevals=len(invalid_ind), **record)
        if verbose:
            print(logbook.stream)

    return population, logbook

# Cell
def eaMuPlusLambdaWithExtraLog(population, toolbox, mu, lambda_, cxpb, mutpb, ngen,
                   stats=None, halloffame=None, verbose=__debug__):
    """Simply modified version of `eaMuPlusLambda` to accept additional return from evaluate function,
    check `eaMuPlusLambda` for documentation."""
    logbook = tools.Logbook()
    logbook.header = ['gen', 'nevals'] + (stats.fields if stats else [])

    # Evaluate the individuals with an invalid fitness
    invalid_ind = [ind for ind in population if not ind.fitness.valid]
    fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
    for ind, fit in zip(invalid_ind, fitnesses):
        if len(fit)==2:
            # Monkey patch the attribute to ind.extra
            ind.fitness.values, ind.extra = fit
        else:
            ind.fitness.values = fit

    if halloffame is not None:
        halloffame.update(population)

    record = stats.compile(population) if stats is not None else {}
    logbook.record(gen=0, nevals=len(invalid_ind), **record)
    if verbose:
        print(logbook.stream)

    # Begin the generational process
    for gen in range(1, ngen + 1):
        # Vary the population
        offspring = algorithms.varOr(population, toolbox, lambda_, cxpb, mutpb)

        # Evaluate the individuals with an invalid fitness
        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            if len(fit)==2:
                # Monkey patch the attribute to ind.extra
                ind.fitness.values, ind.extra = fit
            else:
                ind.fitness.values = fit

        # Update the hall of fame with the generated individuals
        if halloffame is not None:
            halloffame.update(offspring)

        # Select the next generation population
        population[:] = toolbox.select(population + offspring, mu)

        # Update the statistics with the new population
        record = stats.compile(population) if stats is not None else {}
        logbook.record(gen=gen, nevals=len(invalid_ind), **record)
        if verbose:
            print(logbook.stream)

    return population, logbook

# Cell
def eaMuCommaLambdaWithExtraLog(population, toolbox, mu, lambda_, cxpb, mutpb, ngen,
                    stats=None, halloffame=None, verbose=__debug__):
    """Simply modified version of `eaMuCommaLambda` to accept additional return from evaluate function,
    check `eaMuCommaLambda` for documentation.
    """
    assert lambda_ >= mu, "lambda must be greater or equal to mu."

    # Evaluate the individuals with an invalid fitness
    invalid_ind = [ind for ind in population if not ind.fitness.valid]
    fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
    for ind, fit in zip(invalid_ind, fitnesses):
        if len(fit)==2:
            # Monkey patch the attribute to ind.extra
            ind.fitness.values, ind.extra = fit
        else:
            ind.fitness.values = fit

    if halloffame is not None:
        halloffame.update(population)

    logbook = tools.Logbook()
    logbook.header = ['gen', 'nevals'] + (stats.fields if stats else [])

    record = stats.compile(population) if stats is not None else {}
    logbook.record(gen=0, nevals=len(invalid_ind), **record)
    if verbose:
        print(logbook.stream)

    # Begin the generational process
    for gen in range(1, ngen + 1):
        # Vary the population
        offspring = algorithms.varOr(population, toolbox, lambda_, cxpb, mutpb)

        # Evaluate the individuals with an invalid fitness
        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            if len(fit)==2:
                # Monkey patch the attribute to ind.extra
                ind.fitness.values, ind.extra = fit
            else:
                ind.fitness.values = fit

        # Update the hall of fame with the generated individuals
        if halloffame is not None:
            halloffame.update(offspring)

        # Select the next generation population
        population[:] = toolbox.select(offspring, mu)

        # Update the statistics with the new population
        record = stats.compile(population) if stats is not None else {}
        logbook.record(gen=gen, nevals=len(invalid_ind), **record)
        if verbose:
            print(logbook.stream)
    return population, logbook