from deap import base, creator, tools
import random
"""
每个individual是一个list，包含10个元素，需要演化到元素和最小
"""

# ****************************Types********************************
# def create(name, base, **kargs):
# Creates a new class named *name* inheriting from *base*
# A negative weight element corresponds to the minimization of
# the associated objective and positive weight to the maximization.
creator.create("FitnessMin", base.Fitness, weights=(-1.0,))

# an Individual class that is derived from a list with a fitness attribute set
# to the just created fitness
"""
    create("Foo", list, bar=dict, spam=1)

    This above line is exactly the same as defining in the :mod:`creator`
    module something like the following. ::

        class Foo(list):
            spam = 1

            def __init__(self):
                self.bar = dict()
"""
creator.create("Individual", list, fitness=creator.FitnessMin)

# ****************************Initialization********************************
IND_SIZE = 10

toolbox = base.Toolbox()
# def register(self, alias, function, *args, **kargs):
# Register a *function* in the toolbox under the name *alias*.
# *args当function被调用时自动作为function相应参数
"""
            >>> def func(a, b, c=3):
            ...     print(a, b, c)
            ...
            >>> tools = Toolbox()
            >>> tools.register("myFunc", func, 2, c=4)
            >>> tools.myFunc(3)
            2 3 4
"""
toolbox.register("attribute", random.random)

# def initRepeat(container, func, n):
# Call the function *container* with a generator function corresponding
#     to the calling *n* times the function *func*.

"""
        >>> initRepeat(list, random.random, 2) # doctest: +ELLIPSIS,
        ...                                    # doctest: +NORMALIZE_WHITESPACE
        [0.6394..., 0.0250...]
"""
# 将IND_SIZE个 random.random()加入到Individual里，即初始化Individual，每个Individual list里共IND_SIZE个初始值
toolbox.register("individual", tools.initRepeat, creator.Individual,
                 toolbox.attribute, n=IND_SIZE)

# 将individual加入到population里，n由初始化时指定
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

# ****************************Operators********************************


def evaluate(individual):
    # 评估函数为individual里IND_SIZE个值的和
    # 这里，很重要，返回的是(a, )，以为weight是(fitness, )
    # 也可以返回(sum(individual), )
    return sum(individual),


# def cxTwoPoint(ind1, ind2):
# Executes a two-point crossover on the input :term:`sequence` individuals.
toolbox.register("mate", tools.cxTwoPoint)

# gaussian mutation with mu and sigma
# The *indpb* argument is the probability of each attribute to be mutated.
# 元素增减一个小的值
toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=1, indpb=0.1)

# def selTournament(individuals, k, tournsize, fit_attr="fitness"):
# Select the best individual among *tournsize* randomly chosen
# individuals, *k* times. The list returned contains
# references to the input *individuals*.
toolbox.register("select", tools.selTournament, tournsize=3)
toolbox.register("evaluate", evaluate)


def main():
    pop = toolbox.population(n=50)
    CXPB, MUTPB, NGEN = 0.5, 0.2, 40

    # map(func, *iterables) --> map object
    #     Make an iterator that computes the function using arguments from
    #     each of the iterables.
    # map 以参数序列中的每一个元素调用 function 函数，返回包含每次 function 函数返回值的新列表。
    fitnesses = map(toolbox.evaluate, pop)

    for ind, fit in zip(pop, fitnesses):
        ind.fitness.values = fit
        # ind.fitness = fit

    for g in range(NGEN):
        # 每次拿三个，选其中最好的，一直选到len(pop)个
        offspring = toolbox.select(pop, len(pop))
        # 什么的offspring还指向是pop里的同一个对象，需要克隆一下
        # 担心的是一个对象/individual被选到了两次或多次
        offspring = list(map(toolbox.clone, offspring))

        for child1, child2 in zip(offspring[::2], offspring[1::2]):
            if random.random() < CXPB:
                toolbox.mate(child1, child2)
                del child1.fitness.values
                del child2.fitness.values

        for mutant in offspring:
            if random.random() < MUTPB:
                toolbox.mutate(mutant)
                del mutant.fitness.values

        # 上面删除了fitness，下面找出删除的individual，只需要重新评估这些就行，不用重新评估所有
        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = map(toolbox.evaluate, invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            ind.fitness.values = fit

        pop[:] = offspring

    return pop


for ind in main():
    print(evaluate(ind))
