from code.modules.mutator import mutation
import code.model_processing.StringToModel as StringToModel
import numpy as np
from code.structures.Population import Population
def mutate_population(population, number_of_variables, config):
    """
    Perform a mutation operation for superpositions stored in the 'population'.
    The number of mutations is determined by 'mutations_number'
    Inputs:
     population         - list of Models to crossover
     number_of_variables - number of variables required by random_model_generation to build
        appropriate random model
     config.model_generation.mutation_number   - number of required mutations

    Outputs:
     listOfNewModels      - population of new superpositions produced by mutations

    Author: Kulunchakov Andrei, MIPT
    """
    mutations_number = int(config["model_generation"]["mutation_number"])
    # superpositions generated by crossings
    new_generated_superpositions = []

    probabilities = np.exp(-np.linspace(0,1,len(population)))
    probabilities /= sum(probabilities)

    for i in range(mutations_number):
        model_to_mutate = np.random.choice(population, 1, p = probabilities)
        model_to_mutate = model_to_mutate[0].handle
        new_generated_superpositions.append(mutation(model_to_mutate, number_of_variables))

    # convert new strings to a population and append it to the existed one
    listOfNewModels = StringToModel.strings_to_population(new_generated_superpositions)
    list(map(lambda model: setattr(model, 'where_from', "mutate"), listOfNewModels))

    listOfNewModels = [model for model in listOfNewModels if len(model) <= len(model) <= int(config["model_generation"]["maximum_complexity"])]

    return listOfNewModels