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
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
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
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
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