Ejemplo n.º 1
0
def apply(population_size: int, individual_size: int, bounds: np.ndarray,
          func: Callable[[np.ndarray],
                         float], opts: Any, callback: Callable[[Dict], Any],
          lambdas: Union[list, np.array], ng: int, c: Union[int, float],
          p: Union[int, float], max_evals: int, seed: Union[int, None],
          population: Union[np.array,
                            None], answer: Union[None, float,
                                                 int]) -> [np.ndarray, int]:
    """
    Applies the MPEDE differential evolution algorithm.
    :param population_size: Size of the population (NP-max)
    :type population_size: int
    :param ng: Number of generations after the best strategy is updated.
    :type ng: int
    :param lambdas: Percentages of each of the 4 subpopulations.
    :type lambdas: Union[list, np.array]
    :param individual_size: Number of gens/features of an individual.
    :type individual_size: int
    :param bounds: Numpy ndarray with individual_size rows and 2 columns.
    First column represents the minimum value for the row feature.
    Second column represent the maximum value for the row feature.
    :type bounds: np.ndarray
    :param func: Evaluation function. The function used must receive one
     parameter.This parameter will be a numpy array representing an individual.
    :type func: Callable[[np.ndarray], float]
    :param opts: Optional parameters for the fitness function.
    :type opts: Any type.
    :param callback: Optional function that allows read access to the state of all variables once each generation.
    :type callback: Callable[[Dict], Any]
    :param max_evals: Number of evaluations after the algorithm is stopped.
    :type max_evals: int
    :param seed: Random number generation seed. Fix a number to reproduce the
    same results in later experiments.
    :param p: Parameter to choose the best vectors. Must be in (0, 1].
    :type p: Union[int, float]
    :param c: Variable to control parameter adoption. Must be in [0, 1].
    :type c: Union[int, float]
    :type seed: Union[int, None]
    :return: A pair with the best solution found and its fitness.
    :rtype [np.ndarray, int]

    """

    # 0. Check external parameters
    if type(population_size) is not int or population_size <= 0:
        raise ValueError("population_size must be a positive integer.")

    if type(individual_size) is not int or individual_size <= 0:
        raise ValueError("individual_size must be a positive integer.")

    if type(max_evals) is not int or max_evals <= 0:
        raise ValueError("max_evals must be a positive integer.")

    if type(bounds) is not np.ndarray or bounds.shape != (individual_size, 2):
        raise ValueError("bounds must be a NumPy ndarray.\n"
                         "The array must be of individual_size length. "
                         "Each row must have 2 elements.")

    if type(seed) is not int and seed is not None:
        raise ValueError("seed must be an integer or None.")

    if type(p) not in [int, float] and 0 < p <= 1:
        raise ValueError("p must be a real number in (0, 1].")

    if type(c) not in [int, float] and 0 <= c <= 1:
        raise ValueError("c must be an real number in [0, 1].")

    if type(ng) is not int:
        raise ValueError("ng must be a positive integer number.")

    if type(lambdas) not in [list, np.ndarray
                             ] and len(lambdas) != 4 and sum(lambdas) != 1:
        raise ValueError(
            "lambdas must be a list or npdarray of 4 numbers that sum 1.")

    np.random.seed(seed)

    # 1. Initialize internal parameters
    # 1.1 Control parameters
    u_cr = np.ones(3) * 0.5
    u_f = np.ones(3) * 0.5
    f_var = np.zeros(3)
    fes = np.zeros(3)

    # 1.2 Initialize population
    pop_size = lambdas * population_size

    if population is None:
        big_population = commons.init_population(int(sum(pop_size)),
                                                 individual_size, bounds)

    pops = np.array_split(big_population, 4)

    chosen = np.random.randint(0, 3)
    newpop = np.concatenate((pops[chosen], pops[3]))
    pops[chosen] = newpop
    pop_size = list(map(len, pops))
    current_generation = 0
    num_evals = 0

    f = []
    cr = []
    fitnesses = []
    for j in range(3):
        f.append(np.empty(pop_size[j]))
        cr.append(np.empty(pop_size[j]))
        fitnesses.append(commons.apply_fitness(pops[j], func, opts))
        num_evals += len(pops[j])

    # 2. Start the algorithm
    while num_evals <= max_evals:
        current_generation += 1

        # 2.1 Generate CR and F values
        for j in range(3):
            f[j] = scipy.stats.cauchy.rvs(loc=u_f[j],
                                          scale=0.1,
                                          size=len(pops[j]))
            f[j] = np.clip(f[j], 0, 1)

            cr[j] = np.random.normal(u_cr[j], 0.1, len(pops[j]))
            cr[j] = np.clip(cr[j], 0, 1)

        # 2.2 Apply mutation to each subpopulation
        mutated1 = commons.current_to_pbest_mutation(
            pops[0], fitnesses[0], f[0].reshape(len(f[0]), 1),
            np.ones(len(pops[0])) * p, bounds)

        mutated2 = commons.current_to_rand_1_mutation(
            pops[1], fitnesses[1], f[1].copy().reshape(len(f[1]), 1) * .5 + 1,
            f[1].reshape(len(f[1]), 1), bounds)

        mutated3 = commons.binary_mutation(pops[2], f[2].reshape(len(f[2]), 1),
                                           bounds)

        # 2.3 Do the crossover and calculate new fitness
        crossed1 = commons.crossover(pops[0], mutated1,
                                     cr[0].reshape(len(cr[0]), 1))
        crossed2 = mutated2
        crossed3 = commons.crossover(pops[2], mutated3,
                                     cr[2].reshape(len(cr[2]), 1))

        c_fitness1 = commons.apply_fitness(crossed1, func, opts)
        c_fitness2 = commons.apply_fitness(crossed2, func, opts)
        c_fitness3 = commons.apply_fitness(crossed3, func, opts)

        for j in range(3):
            num_evals += len(pops[j])
            fes[j] += len(pops[j])

        # 2.4 Do the selection and update control parameters
        winners1 = c_fitness1 < fitnesses[0]
        winners2 = c_fitness2 < fitnesses[1]
        winners3 = c_fitness3 < fitnesses[2]

        pops[0] = commons.selection(pops[0], crossed1, fitnesses[0],
                                    c_fitness1)
        pops[1] = commons.selection(pops[1], crossed2, fitnesses[1],
                                    c_fitness2)
        pops[2] = commons.selection(pops[2], crossed3, fitnesses[2],
                                    c_fitness3)

        fitnesses[0][winners1] = c_fitness1[winners1]
        fitnesses[1][winners2] = c_fitness2[winners2]
        fitnesses[2][winners3] = c_fitness3[winners3]

        if sum(winners1) != 0 and np.sum(f[0][winners1]) != 0:
            u_cr[0] = (1 - c) * u_cr[0] + c * np.mean(cr[0][winners1])
            u_f[0] = (1 - c) * u_f[0] + c * (np.sum(f[0][winners1]**2) /
                                             np.sum(f[0][winners1]))
        if sum(winners2) != 0 and np.sum(f[1][winners2]) != 0:
            u_cr[1] = (1 - c) * u_cr[1] + c * np.mean(cr[1][winners2])
            u_f[1] = (1 - c) * u_f[1] + c * (np.sum(f[1][winners2]**2) /
                                             np.sum(f[1][winners2]))
        if sum(winners3) != 0 and np.sum(f[2][winners3]) != 0:
            u_cr[2] = (1 - c) * u_cr[2] + c * np.mean(cr[2][winners3])
            u_f[2] = (1 - c) * u_f[2] + c * (np.sum(f[2][winners3]**2) /
                                             np.sum(f[2][winners3]))

        fes[0] += np.sum(fitnesses[0][winners1] - c_fitness1[winners1])
        fes[1] += np.sum(fitnesses[1][winners2] - c_fitness2[winners2])
        fes[2] += np.sum(fitnesses[2][winners3] - c_fitness3[winners3])

        population = np.concatenate((pops[0], pops[1], pops[2]))
        fitness = np.concatenate((fitnesses[0], fitnesses[1], fitnesses[2]))

        if current_generation % ng == 0:
            k = [f_var[i] / len(pops[i] / ng) for i in range(3)]
            chosen = np.argmax(k)

        indexes = np.arange(0, len(population), 1, np.int)
        np.random.shuffle(indexes)
        indexes = np.array_split(indexes, 4)
        chosen = np.random.randint(0, 3)

        pops = []
        fitnesses = []
        f = []
        cr = []

        for j in range(3):

            if j == chosen:
                pops.append(
                    np.concatenate(
                        (population[indexes[j]], population[indexes[3]])))
                fitnesses.append(
                    np.concatenate((fitness[indexes[j]], fitness[indexes[3]])))
            else:
                pops.append(population[indexes[j]])
                fitnesses.append(fitness[indexes[j]])

            f.append(np.empty(len(pops[j])))
            cr.append(np.empty(len(pops[j])))

        if callback is not None:
            callback(**(locals()))

        best = np.argmin(fitness)

        if fitness[best] == answer:
            yield population[best], fitness[best], population, fitness
            break
        else:
            yield population[best], fitness[best], population, fitness
Ejemplo n.º 2
0
def apply(population_size: int, individual_size: int, bounds: np.ndarray,
          func: Callable[[np.ndarray], np.float], opts: Any, memory_size: int,
          callback: Callable[[Dict], Any], max_evals: int, seed: Union[int,
                                                                       None],
          population: Union[np.array,
                            None], answer: Union[None, float,
                                                 int]) -> [np.ndarray, int]:
    """
    Applies the L-SHADE Differential Evolution Algorithm.
    :param population_size: Size of the population.
    :type population_size: int
    :param individual_size: Number of gens/features of an individual.
    :type individual_size: int
    :param bounds: Numpy ndarray with individual_size rows and 2 columns.
    First column represents the minimum value for the row feature.
    Second column represent the maximum value for the row feature.
    :type bounds: np.ndarray
    :param func: Evaluation function. The function used must receive one
     parameter.This parameter will be a numpy array representing an individual.
    :type func: Callable[[np.ndarray], float]
    :param opts: Optional parameters for the fitness function.
    :type opts: Any type.
    :param memory_size: Size of the internal memory.
    :type memory_size: int
    :param callback: Optional function that allows read access to the state of all variables once each generation.
    :type callback: Callable[[Dict], Any]
    :param max_evals: Number of evaluations after the algorithm is stopped.
    :type max_evals: int
    :param seed: Random number generation seed. Fix a number to reproduce the
    same results in later experiments.
    :type seed: Union[int, None]
    :return: A pair with the best solution found and its fitness.
    :rtype [np.ndarray, int]
    """
    # 0. Check parameters are valid
    if type(population_size) is not int or population_size <= 0:
        raise ValueError("population_size must be a positive integer.")

    if type(individual_size) is not int or individual_size <= 0:
        raise ValueError("individual_size must be a positive integer.")

    if type(max_evals) is not int or max_evals <= 0:
        raise ValueError("max_iter must be a positive integer.")

    if type(bounds) is not np.ndarray or bounds.shape != (individual_size, 2):
        raise ValueError("bounds must be a NumPy ndarray.\n"
                         "The array must be of individual_size length. "
                         "Each row must have 2 elements.")

    if type(seed) is not int and seed is not None:
        raise ValueError("seed must be an integer or None.")

    np.random.seed(seed)
    random.seed(seed)

    # 1. Initialization
    if population is None:
        population = commons.init_population(population_size, individual_size,
                                             bounds)
    init_size = population_size
    m_cr = np.ones(memory_size) * 0.5
    m_f = np.ones(memory_size) * 0.5
    archive = []
    k = 0
    fitness = commons.apply_fitness(population, func, opts)

    all_indexes = list(range(memory_size))
    current_generation = 0
    num_evals = population_size
    # Calculate max_iters
    n = population_size
    i = 0
    max_iters = 0

    # Calculate max_iters
    n = population_size
    i = 0
    max_iters = 0

    while i < max_evals:
        max_iters += 1
        n = round((4 - init_size) / max_evals * i + init_size)
        i += n

    while num_evals < max_evals:
        # 2.1 Adaptation
        r = np.random.choice(all_indexes, population_size)
        cr = np.random.normal(m_cr[r], 0.1, population_size)
        cr = np.clip(cr, 0, 1)
        cr[m_cr[r] == 1] = 0
        f = scipy.stats.cauchy.rvs(loc=m_f[r], scale=0.1, size=population_size)
        f[f > 1] = 0

        while sum(f <= 0) != 0:
            r = np.random.choice(all_indexes, sum(f <= 0))
            f[f <= 0] = scipy.stats.cauchy.rvs(loc=m_f[r],
                                               scale=0.1,
                                               size=sum(f <= 0))

        p = np.ones(population_size) * .11

        # 2.2 Common steps
        mutated = commons.current_to_pbest_mutation(population, fitness,
                                                    f.reshape(len(f), 1), p,
                                                    bounds)
        crossed = commons.crossover(population, mutated, cr.reshape(len(f), 1))
        c_fitness = commons.apply_fitness(crossed, func, opts)
        num_evals += population_size
        population, indexes = commons.selection(population,
                                                crossed,
                                                fitness,
                                                c_fitness,
                                                return_indexes=True)

        # 2.3 Adapt for next generation
        archive.extend(population[indexes])

        if len(indexes) > 0:
            if len(archive) > population_size:
                archive = random.sample(archive, population_size)

            weights = np.abs(fitness[indexes] - c_fitness[indexes])
            weights /= np.sum(weights)
            m_cr[k] = np.sum(weights * cr[indexes]**2) / np.sum(
                weights * cr[indexes])
            if np.isnan(m_cr[k]):
                m_cr[k] = 1
            m_f[k] = np.sum(weights * f[indexes]**2) / np.sum(
                weights * f[indexes])
            k += 1
            if k == memory_size:
                k = 0

        fitness[indexes] = c_fitness[indexes]
        # Adapt population size
        new_population_size = round((4 - init_size) / max_evals * num_evals +
                                    init_size)
        if population_size > new_population_size:
            population_size = new_population_size
            best_indexes = np.argsort(fitness)[:population_size]
            population = population[best_indexes]
            fitness = fitness[best_indexes]
            if k == init_size:
                k = 0

        if callback is not None:
            callback(**(locals()))
        current_generation += 1

        best = np.argmin(fitness)

        if fitness[best] == answer:
            yield population[best], fitness[best], population, fitness
            break
        else:
            yield population[best], fitness[best], population, fitness
Ejemplo n.º 3
0
def apply(population_size: int, individual_size: int, f: Union[float, int],
          cr: Union[float,
                    int], bounds: np.ndarray, func: Callable[[np.ndarray],
                                                             float], opts: Any,
          callback: Callable[[Dict], Any], cross: str, max_evals: int,
          seed: Union[int, None], population: Union[np.ndarray, None],
          answer: Union[None, int, float]) -> [np.ndarray, int]:
    """
    Applies the standard differential evolution algorithm.
    :param population_size: Size of the population.
    :type population_size: int
    :param individual_size: Number of gens/features of an individual.
    :type individual_size: int
    :param f: Mutation parameter. Must be in [0, 2].
    :type f: Union[float, int]
    :param cr: Crossover Ratio. Must be in [0, 1].
    :type cr: Union[float, int]
    :param bounds: Numpy ndarray with individual_size rows and 2 columns.
    First column represents the minimum value for the row feature.
    Second column represent the maximum value for the row feature.
    :type bounds: np.ndarray
    :param func: Evaluation function. The function used must receive one
    parameter.This parameter will be a numpy array representing an individual.
    :type func: Callable[[np.ndarray], float]
    :param opts: Optional parameters for the fitness function.
    :type opts: Any type.
    :param callback: Optional function that allows read access to the state of all variables once each generation.
    :type callback: Callable[[Dict], Any]
    :param cross: Indicates whether to use the binary crossover('bin') or the exponential crossover('exp').
    :type cross: str
    :param max_evals: Number of evaluations after the algorithm is stopped.
    :type max_evals: int
    :param seed: Random number generation seed. Fix a number to reproduce the
    same results in later experiments.
    :type seed: Union[int, None]
    :return: A pair with the best solution found and its fitness.
    :rtype [np.ndarray, int]
    """

    # 0. Check parameters are valid
    if type(population_size) is not int or population_size <= 0:
        raise ValueError("population_size must be a positive integer.")

    if type(individual_size) is not int or individual_size <= 0:
        raise ValueError("individual_size must be a positive integer.")

    if (type(f) is not int and type(f) is not float) or not 0 <= f <= 2:
        raise ValueError("f (mutation parameter) must be a "
                         "real number in [0,2].")

    if (type(cr) is not int and type(cr) is not float) or not 0 <= cr <= 1:
        raise ValueError("cr (crossover ratio) must be a "
                         "real number in [0,1].")

    if type(max_evals) is not int or max_evals <= 0:
        raise ValueError("max_evals must be a positive integer.")

    if type(bounds) is not np.ndarray or bounds.shape != (individual_size, 2):
        raise ValueError("bounds must be a NumPy ndarray.\n"
                         "The array must be of individual_size length. "
                         "Each row must have 2 elements.")

    if type(cross) is not str and cross not in ['bin', 'exp']:
        raise ValueError(
            "cross must be a string and must be one of \'bin\' or \'cross\'")
    if type(seed) is not int and seed is not None:
        raise ValueError("seed must be an integer or None.")

    # 1. Initialization
    np.random.seed(seed)
    if population is None:
        population = commons.init_population(population_size, individual_size,
                                             bounds)

    #population = commons.init_population(population_size, individual_size, bounds)
    try:
        fitness = commons.apply_fitness(population, func, opts)
    except TypeError:
        print(func, population)

    #use self.population and self.fitness - move the loop into a step function

    max_iters = max_evals // population_size
    #print("de : ", max_iters)
    results = []
    for current_generation in range(max_iters):

        mutated = commons.binary_mutation(population, f, bounds)
        if cross == 'bin':
            crossed = commons.crossover(population, mutated, cr)
        else:
            crossed = commons.exponential_crossover(population, mutated, cr)

        c_fitness = commons.apply_fitness(crossed, func, opts)
        population, indexes = commons.selection(population,
                                                crossed,
                                                fitness,
                                                c_fitness,
                                                return_indexes=True)

        fitness[indexes] = c_fitness[indexes]

        #print(locals())

        best = np.argmin(fitness)

        if callback is not None:
            callback(**(locals()))

        best = np.argmin(fitness)

        if fitness[best] == answer:
            results.append(
                (population[best], fitness[best], population, fitness))
            break
        else:
            results.append(
                (population[best], fitness[best], population, fitness))
    return results
Ejemplo n.º 4
0
def apply(population_size: int, individual_size: int, bounds: np.ndarray,
          func: Callable[[np.ndarray], float], opts: Any, p: Union[int, float],
          c: Union[int, float], callback: Callable[[Dict], Any],
          max_evals: int, seed: Union[int, None], population: Union[np.array,
                                                                    None],
          answer: Union[None, int, float]) -> [np.ndarray, int]:
    """
    Applies the JADE Differential Evolution algorithm.
    :param population_size: Size of the population.
    :type population_size: int
    :param individual_size: Number of gens/features of an individual.
    :type individual_size: int
    :param bounds: Numpy ndarray with individual_size rows and 2 columns.
    First column represents the minimum value for the row feature.
    Second column represent the maximum value for the row feature.
    :type bounds: np.ndarray
    :param func: Evaluation function. The function used must receive one
     parameter.This parameter will be a numpy array representing an individual.
    :type func: Callable[[np.ndarray], float]
    :param opts: Optional parameters for the fitness function.
    :type opts: Any type.
    :param p: Parameter to choose the best vectors. Must be in (0, 1].
    :type p: Union[int, float]
    :param c: Variable to control parameter adoption. Must be in [0, 1].
    :type c: Union[int, float]
    :param callback: Optional function that allows read access to the state of all variables once each generation.
    :type callback: Callable[[Dict], Any]
    :param max_evals: Number of evaluations after the algorithm is stopped.
    :type max_evals: int
    :param seed: Random number generation seed. Fix a number to reproduce the
    same results in later experiments.
    :type seed: Union[int, None]
    :return: A pair with the best solution found and its fitness.
    :rtype [np.ndarray, int]
    """
    # 0. Check parameters are valid
    if type(population_size) is not int or population_size <= 0:
        raise ValueError("population_size must be a positive integer.")

    if type(individual_size) is not int or individual_size <= 0:
        raise ValueError("individual_size must be a positive integer.")

    if type(max_evals) is not int or max_evals <= 0:
        raise ValueError("max_evals must be a positive integer.")

    if type(bounds) is not np.ndarray or bounds.shape != (individual_size, 2):
        raise ValueError("bounds must be a NumPy ndarray.\n"
                         "The array must be of individual_size length. "
                         "Each row must have 2 elements.")

    if type(seed) is not int and seed is not None:
        raise ValueError("seed must be an integer or None.")

    if type(p) not in [int, float] and 0 < p <= 1:
        raise ValueError("p must be a real number in (0, 1].")
    if type(c) not in [int, float] and 0 <= c <= 1:
        raise ValueError("c must be an real number in [0, 1].")

    np.random.seed(seed)

    # 1. Init population
    if population is None:
        population = commons.init_population(population_size, individual_size,
                                             bounds)
    u_cr = 0.5
    u_f = 0.6

    p = np.ones(population_size) * p
    fitness = commons.apply_fitness(population, func, opts)
    max_iters = max_evals // population_size
    #print("jade : ", max_iters)
    results = []
    for current_generation in range(max_iters):
        # 2.1 Generate parameter values for current generation
        cr = np.random.normal(u_cr, 0.1, population_size)
        f = np.random.rand(population_size // 3) * 1.2
        f = np.concatenate(
            (f,
             np.random.normal(u_f, 0.1,
                              population_size - (population_size // 3))))

        # 2.2 Common steps
        mutated = commons.current_to_pbest_mutation(population, fitness,
                                                    f.reshape(len(f), 1), p,
                                                    bounds)
        crossed = commons.crossover(population, mutated, cr.reshape(len(f), 1))
        c_fitness = commons.apply_fitness(crossed, func, opts)
        population, indexes = commons.selection(population,
                                                crossed,
                                                fitness,
                                                c_fitness,
                                                return_indexes=True)

        # 2.3 Adapt for next generation
        if len(indexes) != 0:
            u_cr = (1 - c) * u_cr + c * np.mean(cr[indexes])
            u_f = (1 - c) * u_f + c * (np.sum(f[indexes]**2) /
                                       np.sum(f[indexes]))

        fitness[indexes] = c_fitness[indexes]
        if callback is not None:
            callback(**(locals()))

        best = np.argmin(fitness)

        if fitness[best] == answer:
            results.append(
                (population[best], fitness[best], population, fitness))
            break
        else:
            results.append(
                (population[best], fitness[best], population, fitness))
    return results
Ejemplo n.º 5
0
def apply(population_size: int, individual_size: int, bounds: np.ndarray,
          func: Callable[[np.ndarray], float], opts: Any,
          callback: Callable[[Dict], Any], max_evals: int, seed: Union[int,
                                                                       None],
          answer: Union[None, int, float], population: [np.ndarray, None]):
    """
    Applies the Self-adaptive differential evolution algorithm (SaDE).
    :param population_size: Size of the population.
    :type population_size: int
    :param individual_size: Number of gens/features of an individual.
    :type individual_size: int
    :param bounds: Numpy ndarray with individual_size rows and 2 columns.
    First column represents the minimum value for the row feature.
    Second column represent the maximum value for the row feature.
    :type bounds: np.ndarray
    :param func: Evaluation function. The function used must receive one
     parameter.This parameter will be a numpy array representing an individual.
    :type func: Callable[[np.ndarray], float]
    :param opts: Optional parameters for the fitness function.
    :type opts: Any type.
    :param callback: Optional function that allows read access to the state of all variables once each generation.
    :type callback: Callable[[Dict], Any]
    :param max_evals: Number of evaluatios after the algorithm is stopped.
    :type max_evals: int
    :param seed: Random number generation seed. Fix a number to reproduce the
    same results in later experiments.
    :type seed: Union[int, None]
    :return: A pair with the best solution found and its fitness.
    :rtype [np.ndarray, int]
    """

    if type(population_size) is not int or population_size <= 0:
        raise ValueError("population_size must be a positive integer.")

    if type(individual_size) is not int or individual_size <= 0:
        raise ValueError("individual_size must be a positive integer.")

    if type(bounds) is not np.ndarray or bounds.shape != (individual_size, 2):
        raise ValueError("bounds must be a NumPy ndarray.\n"
                         "The array must be of individual_size length. "
                         "Each row must have 2 elements.")

    if type(max_evals) is not int or max_evals <= 0:
        raise ValueError("max_evals must be a positive integer.")

    if type(seed) is not int and seed is not None:
        raise ValueError("seed must be an integer or None.")

    # 1. Initialization
    np.random.seed(seed)
    if population is None:
        population = commons.init_population(population_size, individual_size,
                                             bounds)

    # 2. SaDE Algorithm
    probability = 0.5
    fitness = commons.apply_fitness(population, func, opts)
    cr_m = 0.5
    f_m = 0.5

    sum_ns1 = 0
    sum_nf1 = 0
    sum_ns2 = 0
    sum_nf2 = 0
    cr_list = []

    f = np.random.normal(f_m, 0.3, population_size)
    f = np.clip(f, 0, 2)

    cr = np.random.normal(cr_m, 0.1, population_size)
    cr = np.clip(cr, 0, 1)

    max_iters = max_evals // population_size

    #print("sade : ", max_iters)
    results = []
    for current_generation in range(max_iters):
        # 2.1 Mutation
        # 2.1.1 Randomly choose which individuals do each mutation
        choice = np.random.rand(population_size)
        choice_1 = choice < probability
        choice_2 = choice >= probability

        # 2.1.2 Apply the mutations
        mutated = population.copy()
        mutated[choice_1] = commons.binary_mutation(
            population[choice_1], f[choice_1].reshape(sum(choice_1), 1),
            bounds)
        mutated[choice_2] = commons.current_to_best_2_binary_mutation(
            population[choice_2], fitness[choice_2],
            f[choice_2].reshape(sum(choice_2), 1), bounds)

        # 2.2 Crossover
        crossed = commons.crossover(population, mutated,
                                    cr.reshape(population_size, 1))
        c_fitness = commons.apply_fitness(crossed, func, opts)

        # 2.3 Selection
        population = commons.selection(population, crossed, fitness, c_fitness)
        winners = c_fitness < fitness
        fitness[winners] = c_fitness[winners]

        # 2.4 Self Adaption
        chosen_1 = np.sum(np.bitwise_and(choice_1, winners))
        chosen_2 = np.sum(np.bitwise_and(choice_2, winners))
        sum_ns1 += chosen_1
        sum_ns2 += chosen_2
        sum_nf1 += np.sum(choice_1) - chosen_1
        sum_nf2 += np.sum(choice_2) - chosen_2
        cr_list = np.concatenate((cr_list, cr[winners]))

        # 2.4.1 Adapt mutation strategy probability
        if (current_generation + 1) % 50 == 0:
            probability = sum_ns1 * (sum_ns2 + sum_nf2) / (sum_ns2 *
                                                           (sum_ns1 + sum_nf1))
            probability = np.clip(probability, 0, 1)
            if np.isnan(probability):
                probability = .99
            sum_ns1 = 0
            sum_ns2 = 0
            sum_nf1 = 0
            sum_nf2 = 0

        # 2.4.2
        if (current_generation + 1) % 25 == 0:
            if len(cr_list) != 0:
                cr_m = np.mean(cr_list)
                cr_list = []
            cr = np.random.normal(cr_m, 0.1, population_size)
            cr = np.clip(cr, 0, 1)

        if callback is not None:
            callback(**(locals()))

        best = np.argmin(fitness)

        if fitness[best] == answer:
            results.append(
                (population[best], fitness[best], population, fitness))
            break
        else:
            results.append(
                (population[best], fitness[best], population, fitness))
    return results