Source code for neorl.hybrid.edev

#    This file is part of NEORL.

#    Copyright (c) 2021 Exelon Corporation and MIT Nuclear Science and Engineering
#    NEORL is free software: you can redistribute it and/or modify
#    it under the terms of the MIT LICENSE

#    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#    SOFTWARE.

# -*- coding: utf-8 -*-
#"""
#Created on Thu Dec  3 14:42:29 2020
#
#@author: Majdi
#"""

import random
import numpy as np
from numpy import arange, multiply, zeros, copy
import joblib
from neorl.hybrid.edevcore.helpers import ARCHIVE, generator, sub2ind, gnR1R2, randFCR
from neorl.evolu.discrete import mutate_discrete, encode_grid_to_discrete 
from neorl.evolu.discrete import decode_discrete_to_grid, encode_grid_indv_to_discrete
from neorl.utils.seeding import set_neorl_seed
from neorl.utils.tools import get_population, check_mixed_individual


[docs]class EDEV(object): """ Ensemble of differential evolution variants :param mode: (str) problem type, either ``min`` for minimization problem or ``max`` for maximization :param bounds: (dict) input parameter type and lower/upper bounds in dictionary form. Example: ``bounds={'x1': ['int', 1, 4], 'x2': ['float', 0.1, 0.8], 'x3': ['float', 2.2, 6.2]}`` :param fit: (function) the fitness function :param npop: (int): total size of the full population, which will be divided into three sub-populations :param int_transform: (str): method of handling int/discrete variables, choose from: ``nearest_int``, ``sigmoid``, ``minmax``. :param ncores: (int) number of parallel processors (must be ``<= npop``) :param seed: (int) random seed for sampling """ #:param lambda_: (float): fraction of ``npop`` to split into 3 sub-populations. ``pop1 = npop - npop * lambda_``, ``pop2 = pop3 = npop * lambda_`` (see **Notes** below) def __init__(self, mode, bounds, fit, npop=100, int_transform='nearest_int', ncores=1, seed=None): set_neorl_seed(seed) lambda_=0.1 #for EDEV stability, better to keep this parameter fixed. assert npop >= 70, '--error: EDEV ensemble requires a large population of 70 or more' assert ncores <= npop, '--error: ncores ({}) must be less than or equal than npop ({})'.format(ncores, npop) assert 0 < lambda_ < 1, '--error: lambda_ must be more than 0 and less than 1' #--mir self.mode=mode if mode == 'min': self.fit=fit elif mode == 'max': def fitness_wrapper(*args, **kwargs): return -fit(*args, **kwargs) self.fit=fitness_wrapper else: raise ValueError('--error: The mode entered by user is invalid, use either `min` or `max`') self.int_transform=int_transform if int_transform not in ["nearest_int", "sigmoid", "minmax"]: raise ValueError('--error: int_transform entered by user is invalid, must be `nearest_int`, `sigmoid`, or `minmax`') self.bounds=bounds self.ncores = ncores self.npop=npop self.lambda_=lambda_ #infer variable types self.var_type = np.array([bounds[item][0] for item in bounds]) #mir-grid if "grid" in self.var_type: self.grid_flag=True self.orig_bounds=bounds #keep original bounds for decoding #print('--debug: grid parameter type is found in the space') self.bounds, self.bounds_map=encode_grid_to_discrete(self.bounds) #encoding grid to int #define var_types again by converting grid to int self.var_type = np.array([self.bounds[item][0] for item in self.bounds]) else: self.grid_flag=False self.bounds = bounds self.orig_bounds=bounds self.dim = len(bounds) self.lb=np.array([self.bounds[item][1] for item in self.bounds]) self.ub=np.array([self.bounds[item][2] for item in self.bounds]) slice_index=int(self.lambda_*self.npop) if slice_index <= 6: raise Exception('--error: the size of pop2 and pop3 ({}) must be more than 6 individuals. Make sure int(lambda_ * npop) >= 7'.format(slice_index)) def init_sample(self, bounds): indv=[] for key in bounds: if bounds[key][0] == 'int': indv.append(random.randint(bounds[key][1], bounds[key][2])) elif bounds[key][0] == 'float': indv.append(random.uniform(bounds[key][1], bounds[key][2])) #elif bounds[key][0] == 'grid': # indv.append(random.sample(bounds[key][1],1)[0]) else: raise Exception ('unknown data type is given, either int, float, or grid are allowed for parameter bounds') return np.array(indv) def eval_pop(self, pop_to_evaluate): #--------------------- # Fitness calcs #--------------------- core_lst=[] for case in range (0, pop_to_evaluate.shape[0]): core_lst.append(pop_to_evaluate[case, :]) if self.ncores > 1: with joblib.Parallel(n_jobs=self.ncores) as parallel: fitness_lst=parallel(joblib.delayed(self.fit_worker)(item) for item in core_lst) else: fitness_lst=[] for item in core_lst: fitness_lst.append(self.fit_worker(item)) return fitness_lst def select(self, pos, fit): best_fit=np.min(fit) min_idx=np.argmin(fit) best_pos=pos[min_idx,:].copy() return best_pos, best_fit , min_idx def ensure_bounds(self, vec): vec_new = [] # cycle through each variable in vector for i, (key, val) in enumerate(self.bounds.items()): # variable exceedes the minimum boundary if vec[i] < self.bounds[key][1]: vec_new.append(self.bounds[key][1]) # variable exceedes the maximum boundary if vec[i] > self.bounds[key][2]: vec_new.append(self.bounds[key][2]) # the variable is fine if self.bounds[key][1] <= vec[i] <= self.bounds[key][2]: vec_new.append(vec[i]) return vec_new def fit_worker(self, x): #This worker is for parallel calculations # Clip the position outside the lower/upper bounds and return same position x=self.ensure_bounds(x) if self.grid_flag: #decode the individual back to the int/float/grid mixed space x=decode_discrete_to_grid(x,self.orig_bounds,self.bounds_map) # Calculate objective function for each search agent fitness = self.fit(x) self.FES += 1 return fitness def ensure_discrete(self, vec): #""" #to mutate a vector if discrete variables exist #handy function to be used within SSA phases #Params: #vec - position in vector/list form #Return: #vec - updated position vector with discrete values #""" for dim in range(self.dim): if self.var_type[dim] == 'int': vec[dim] = mutate_discrete(x_ij=vec[dim], x_min=min(vec), x_max=max(vec), lb=self.lb[dim], ub=self.ub[dim], alpha=self.a, method=self.int_transform, ) return vec
[docs] def evolute(self, ngen, ng=20, x0=None, verbose=False): """ This function evolutes the EDEV algorithm for a number of generations. :param ngen: (int) number of generations to evolute :param ng: (int) the period or number of generations to determine the best performing DE variant and reward subpopulation assignment, ``ng < ngen`` (see **Notes** below for more info). :param x0: (list of lists) initial position of the individuals (must be of same size as ``npop``) :param verbose: (bool) print statistics to screen :return: (tuple) (best individual, best fitness, and dictionary containing major search results) """ assert ng < ngen, '--error: ng must be less than ngen to allow frequent updates' self.history = {'global_fitness':[], 'JADE':[], 'CoDE':[], 'EPSDE':[]} self.best_fitness=float("inf") self.verbose=verbose self.a=1 #for discrete analysis self.mixPop = np.zeros((self.npop, self.dim)) if x0: assert len(x0) == self.npop, '--error: the length of x0 ({}) MUST equal the number of npop in the group ({})'.format(len(x0), self.npop) for i in range(self.npop): check_mixed_individual(x=x0[i], bounds=self.orig_bounds) #assert the type provided is consistent if self.grid_flag: self.mixPop[i,:] = encode_grid_indv_to_discrete(x0[i], bounds=self.orig_bounds, bounds_map=self.bounds_map) else: self.mixPop[i,:] = x0[i] else: # Initialize the positions for i in range(self.npop): self.mixPop[i,:]=self.init_sample(self.bounds) #--------------------------------------------------------------------------- #-------------------------------EDEV starts here---------------------------- #--------------------------------------------------------------------------- # Define the dimension of the problem self.FES=0 arrayGbestChange=np.ones((3), dtype=float) arrayGbestChangeRate=np.zeros((3), dtype=float) indexBestLN=0 numViaLN=np.array([0,0,0]) rateViaLN=zeros((ngen,len(numViaLN))) #evaluate the first population #for discrete mutation for iii in range(self.mixPop.shape[0]): self.mixPop[iii, :] = self.ensure_bounds(self.mixPop[iii, :]) self.mixPop[iii, :] = self.ensure_discrete(self.mixPop[iii, :]) mixVal=np.array(self.eval_pop(self.mixPop)) self.best_position, self.overallBestVal, _ = self.select(self.mixPop, mixVal) permutation=np.random.permutation(self.npop) slice_index=int(self.lambda_*self.npop) arrayThird=permutation[:slice_index] #for EPSDE arraySecond=permutation[slice_index : 2*slice_index] #for CoDE arrayFirst=permutation[2*slice_index:] #for JADE if verbose: print('--debug: initial size of EDEV population is %i'%self.npop) print('--debug: initial size of pop1 is %i'%len(arrayFirst)) print('--debug: initial size of pop2 is %i'%len(arraySecond)) print('--debug: initial size of pop3 is %i'%len(arrayThird)) ## Initialize JADE related popold=self.mixPop[arrayFirst,:] valParents=mixVal[arrayFirst] c=1 / 10 pj=0.05 CRm=0.5 Fm=0.5 #initialize archive archive=ARCHIVE(NP=len(arrayFirst), dim=self.dim) #the values and indices of the best solutions #valBest=np.sort(valParents) indBest=np.argsort(valParents) #Initialize CoDE related popCODE=self.mixPop[arraySecond,:] valCODE=mixVal[arraySecond] #Initialize EPSDE related I_D=self.dim FM_pop=self.mixPop[arrayThird,:] FM_popold=zeros(FM_pop.shape) I_NP=len(arrayThird) val=zeros((I_NP)) self.FVr_bestmem=zeros((I_D)) #FVr_bestmemit=zeros((I_D)) I_nfeval=0 RATE=0 #------Evaluate the best member after initialization---------------------- #for discrete mutation for iii in range(FM_pop.shape[0]): FM_pop[iii, :] = self.ensure_bounds(FM_pop[iii, :]) FM_pop[iii, :] = self.ensure_discrete(FM_pop[iii, :]) val=np.array(self.eval_pop(FM_pop)) self.FVr_bestmem, F_bestval, I_best_index=self.select(pos=FM_pop, fit=val) #------DE-Minimization--------------------------------------------- #------FM_popold is the population which has to compete. It is-------- #------static through one iteration. FM_pop is the newly-------------- #------emerging population.---------------------------------------- I_iter=0 FF=np.array([0.4, 0.5, 0.6, 0.7, 0.8, 0.9]) CR=np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]) spara=np.random.permutation(3) PPara=np.empty((0,3), dtype=float) inde=np.arange(0,I_NP) RR=zeros((self.npop)) gen=0 FESj=0 goodCR=[] goodF=[] rate=[] while gen < ngen: self.a= 1 - gen * ((1) / ngen) #mir: a decreases linearly between 1 to 0, for discrete mutation ## update the index for adjust different algorithms if gen % ng == 0: arrayGbestChangeRate[0]=arrayGbestChange[0] / len(arrayFirst) arrayGbestChangeRate[1]=arrayGbestChange[1] / (len(arraySecond)*3) arrayGbestChangeRate[2]=arrayGbestChange[2] / len(arrayThird) indexBestLN=np.argmax(arrayGbestChangeRate) arrayGbestChange=np.ones((3), dtype=float) arrayGbestChangeRate=np.zeros((3), dtype=float) permutation=np.random.permutation(self.npop) if indexBestLN == 0: arrayThird=permutation[:slice_index] arraySecond=permutation[slice_index : 2*slice_index] arrayFirst=permutation[2*slice_index:] numViaLN[0]=numViaLN[0] + 1 else: if indexBestLN == 1: arrayThird=permutation[:slice_index] arrayFirst=permutation[slice_index : 2*slice_index] arraySecond=permutation[2*slice_index:] numViaLN[1]=numViaLN[1] + 1 else: if indexBestLN == 2: arrayFirst=permutation[:slice_index] arraySecond=permutation[slice_index : 2*slice_index] arrayThird=permutation[2*slice_index:] numViaLN[2]=numViaLN[2] + 1 rateViaLN[gen,:]=numViaLN / sum(numViaLN) #---------------------------- #JADE #---------------------------- pop=self.mixPop[arrayFirst,:] valParents=mixVal[arrayFirst] popsize=len(arrayFirst) if FESj > 1 and len(goodCR) > 0 and sum(goodF) > 0: CRm = (1 - c)*CRm + c * np.mean(goodCR) Fm = (1 - c)*Fm + c * sum(goodF ** 2) / sum(goodF) # Generate CR according to a normal distribution with mean CRm, and std 0.1 # Generate F according to a cauchy distribution with location parameter Fm, and scale parameter 0.1 Fj,CRj=randFCR(popsize,CRm,0.1,Fm,0.1) r0=arange(0,popsize) if archive.pop.size == 0: popAll=copy(pop) else: popAll=np.concatenate((pop, archive.pop),axis=0) r1,r2=gnR1R2(popsize-1,popAll.shape[0]-1,r0) #-1 for python indexing from 0 indBest=np.argsort(valParents) pNP=max(round(pj*popsize),2) #choose at least two best solutions randindex=np.ceil(np.random.uniform(size=(popsize))*pNP) #select from [1, 2, 3, ..., pNP] randindex[randindex < 1]=1 #to avoid the problem that rand = 0 and thus ceil(rand) = 0 randindex=randindex-1 #for python indexing assert np.min(randindex) >= 0, '--error: one of the indices in randindex is less than 0, the alg does not function properly' pbest=pop[indBest[randindex.astype(int)],:] #randomly choose one of the top 100p% solutions # == == == == == == == == == == == == == == == Mutation == == == == == == == == == == == == == Ftr=Fj.reshape(Fj.shape[0],1) #transform Fj Ftr=np.tile(Ftr,(1,self.dim)) #convert Fj from npop dimensions to a npop x dim with fixed value in all dimensions vi=pop + multiply(Ftr,(pbest - pop + pop[r1,:] - popAll[r2,:])) #apply bound constraint check for iii in range(len(vi)): vi[iii,:]=self.ensure_bounds(vi[iii,:]) # == == == == = Crossover == == == == = CRtr=CRj.reshape(CRj.shape[0],1) #transform Crj CRtr=np.tile(CRtr,(1,self.dim)) #convert Fj from npop dimensions to a npop x dim with fixed value in all dimensions mask=np.random.uniform(size=(popsize,self.dim)) > CRtr #mask is used to indicate which elements of ui comes from the parent rows=arange(popsize) cols=np.array(np.floor(np.random.uniform(size=(popsize)) * self.dim) + 1, dtype=int) jrand=np.array(sub2ind((popsize,self.dim),rows,cols),dtype=int) mask=mask.reshape(-1) mask[jrand]=False mask=mask.reshape(popsize,self.dim) ui=copy(vi) ui[mask]=pop[mask] #evaluate the offspring #for discrete mutation for iii in range(ui.shape[0]): ui[iii, :] = self.ensure_bounds(ui[iii, :]) ui[iii, :] = self.ensure_discrete(ui[iii, :]) valOffspring=np.array(self.eval_pop(ui)) #== == == == == == == == == == == == == == == Selection == == == == == == == == == == == == == # I == 1: the parent is better; I == 2: the offspring is better valParents=np.minimum(valParents, valOffspring) I= np.where(valOffspring <= valParents)[0] #indices where offspring is better than the parents popold=copy(pop) archive.UpdateArchive(popold[I,:],valParents[I]) popold[I,:]=ui[I,:] goodCR=CRj[I] goodCR=Fj[I] if np.min(valParents) < self.overallBestVal: self.overallBestVal=np.min(valParents) min_best_index=np.argmin(valParents) self.best_position=popold[min_best_index,:].copy() self.jade_fitness=np.min(valParents) #store best JADE fitness arrayGbestChange[0]=arrayGbestChange[0] + np.sum(mixVal[arrayFirst] - valParents) self.mixPop[arrayFirst,:]=popold mixVal[arrayFirst]=valParents #---------------------------- #CoDE #---------------------------- popCODE=self.mixPop[arraySecond,:] valCODE=mixVal[arraySecond] popsizeC=len(arraySecond) pTemp=copy(popCODE) fitTemp=copy(valCODE) uSet=zeros((3*popsizeC,self.dim)) for i in range(popsizeC): F=np.array([1.0,1.0,0.8]) CRC=np.array([0.1,0.9,0.2]) #Uniformly and randomly select one of the control # parameter settings for each trial vector generation strategy paraIndex=np.floor(np.random.uniform(size=(3))*len(F)) u=generator(popCODE, self.bounds, i, F, CRC, popsizeC, self.dim, paraIndex.astype(int)) uSet[(i+1)*3 - 3 : 3*(i+1),:]=copy(u) # Evaluate the trial vectors for iii in range(uSet.shape[0]): #discrete mutation uSet[iii, :] = self.ensure_bounds(uSet[iii, :]) uSet[iii, :] = self.ensure_discrete(uSet[iii, :]) fitSet=np.array(self.eval_pop(uSet)) for i in range(popsizeC): #minVal=np.min(fitSet[(i+1)*3 - 3 : 3*(i+1)]) minID=np.argmin(fitSet[(i+1)*3 - 3 : 3*(i+1)]) bestInd=uSet[3*i + minID,:] bestIndFit=fitSet[3*i + minID] # Choose the better one between the trial vector and the target vector if valCODE[i] >= bestIndFit: pTemp[i,:]=bestInd.copy() fitTemp[i]=bestIndFit popCODE=copy(pTemp) valCODE=copy(fitTemp) if np.min(fitTemp) < self.overallBestVal: self.overallBestVal=np.min(fitTemp) min_best_index=np.argmin(fitTemp) self.best_position=popCODE[min_best_index,:].copy() self.code_fitness=np.min(fitTemp) #store best CoDE fitness arrayGbestChange[1]=arrayGbestChange[1] + np.sum(mixVal[arraySecond] - valCODE) self.mixPop[arraySecond,:]=popCODE mixVal[arraySecond]=valCODE #---------------------------- #EPSDE #---------------------------- FM_pop=self.mixPop[arrayThird,:] val=mixVal[arrayThird] I_NP=len(arrayThird) inde=arange(I_NP) Para=zeros((I_NP,3)) I_iter=gen if (I_iter == 0 or len(Para) < I_NP): Para[:,0]=spara[np.random.randint(spara.shape[0],size=inde.shape[0])] Para[:,1]=CR[np.random.randint(CR.shape[0],size=inde.shape[0])] Para[:,2]=FF[np.random.randint(FF.shape[0],size=inde.shape[0])] else: for k in range(len(inde)): if (np.random.uniform() <= RATE and len(PPara) > 0) or len(RR) < I_NP: RR[k]=np.random.randint(PPara.shape[0]) Para[inde[k],:]=PPara[np.random.randint(PPara.shape[0]),:] else: RR[k]=0 Para[inde[k],:]=[ spara[np.random.randint(spara.shape[0])], CR[np.random.randint(CR.shape[0])], FF[np.random.randint(FF.shape[0])] ] RRR=[] count=0 FM_popold=copy(FM_pop) par=np.zeros((I_NP, I_D)) FM_ui=par.copy() for i in range(I_NP): FM_mui=np.random.uniform(size=(I_D)) < Para[i,1] dd=np.where(FM_mui == True)[0] if len(dd) == 0: ddd=int(np.ceil(np.random.uniform() * I_D) - 1) FM_mui[ddd]=1 FM_mpo=FM_mui < 0.5 FM_bm=copy(self.FVr_bestmem) par[i,:]= np.random.normal(Para[i,2],0.001,size=(I_D)) if (Para[i,0] == 0): #DE/best/2/bin ind=np.random.permutation(I_NP) FM_pm3=FM_popold[ind[0],:] FM_pm4=FM_popold[ind[1],:] FM_pm5=FM_popold[ind[2],:] FM_pm6=FM_popold[ind[3],:] FM_ui[i,:]=FM_bm + (FM_pm3 - FM_pm4 + FM_pm5 - FM_pm6) * par[i,:] FM_ui[i,:]=multiply(FM_popold[i,:],FM_mpo) + multiply(FM_ui[i,:],FM_mui) if (Para[i,0] == 1): #DE/rand/1/bin ind=np.random.permutation(I_NP) FM_pm7=FM_popold[ind[0],:] FM_pm8=FM_popold[ind[1],:] FM_pm9=FM_popold[ind[2],:] FM_ui[i,:]=FM_pm7 + par[i,:] * (FM_pm8 - FM_pm9) FM_ui[i,:]=multiply(FM_popold[i,:],FM_mpo) + multiply(FM_ui[i,:],FM_mui) if (Para[i,0] == 2): # DE/current-to-rand/1/bin/ ind=np.random.permutation(I_NP) FM_pm21=FM_popold[ind[0],:] FM_pm22=FM_popold[ind[1],:] FM_pm23=FM_popold[ind[2],:] FM_ui[i,:]=FM_popold[i,:] + np.random.uniform(size=I_D) * (FM_pm21 - FM_popold[i,:]) + par[i,:] * (FM_pm22 - FM_pm23) if np.any(FM_ui[i,:] < self.lb) or np.any(FM_ui[i,:] > self.ub): #reset an individual outside the bounds FM_ui[i,:]=self.lb + multiply((self.ub - self.lb),np.random.uniform(size=I_D)) #evaluate the updated population for iii in range(FM_ui.shape[0]): #discrete mutation FM_ui[iii, :] = self.ensure_bounds(FM_ui[iii, :]) FM_ui[iii, :] = self.ensure_discrete(FM_ui[iii, :]) tempval=np.array(self.eval_pop(FM_ui)) for i in range(I_NP): I_nfeval=I_nfeval + 1 if (tempval[i] < val[i]): FM_pop[i,:]=FM_ui[i,:] val[i]=tempval[i] PPara=np.append(PPara, Para[i,:].reshape(1,-1), axis=0) if RR[i] != 0: RRR.append(int(RR[i])) if tempval[i] < F_bestval: F_bestval=tempval[i] self.FVr_bestmem=FM_ui[i,:] I_best_index=i else: count=count + 1 if np.min(val) < self.overallBestVal: self.overallBestVal=np.min(val) min_best_index=np.argmin(val) self.best_position=FM_pop[min_best_index,:].copy() self.epsde_fitness=np.min(val) #store best EPSDE fitness arrayGbestChange[2]=arrayGbestChange[2] + np.sum(mixVal[arrayThird] - val) self.mixPop[arrayThird,:]=FM_pop mixVal[arrayThird]=val PPara = np.delete(PPara, RRR, axis=0) rate.append(count / I_NP) if I_iter > 10: RATE=np.mean(rate[(I_iter - 10) : I_iter]) else: RATE=np.mean(rate) I_iter=I_iter + 1 gen=gen + 1 #--------------------------------------------------------------------------- #-------------------------------EDEV ends here---------------------------- #--------------------------------------------------------------------------- #--mir if self.mode=='max': self.fitness_best_correct=-self.overallBestVal self.jade_fitness=-self.jade_fitness self.code_fitness=-self.code_fitness self.epsde_fitness=-self.epsde_fitness else: self.fitness_best_correct=self.overallBestVal self.last_pop=self.mixPop.copy() self.last_fit=np.array(mixVal).copy() self.history['global_fitness'].append(self.fitness_best_correct) self.history['JADE'].append(self.jade_fitness) self.history['CoDE'].append(self.code_fitness) self.history['EPSDE'].append(self.epsde_fitness) # Print statistics if self.verbose: print('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^') print('EDEV step {}/{}, npop={}, Ncores={}'.format((gen)*self.npop, ngen*self.npop, self.npop, self.ncores)) print('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^') print('Best EDEV Fitness:', np.round(self.fitness_best_correct,6)) if self.grid_flag: self.ind_decoded = decode_discrete_to_grid(self.best_position, self.orig_bounds, self.bounds_map) print('Best Position:', self.ind_decoded) else: print('Best Position:', self.best_position) print('JADE Fitness:', self.jade_fitness) print('CoDE Fitness:', self.code_fitness) print('EPSDE Fitness:', self.epsde_fitness) print('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^') #mir-grid if self.grid_flag: self.ind_correct = decode_discrete_to_grid(self.best_position, self.orig_bounds, self.bounds_map) else: self.ind_correct = self.best_position if self.mode=='max': self.last_fit=-self.last_fit #--mir return the last population for restart calculations if self.grid_flag: self.history['last_pop'] = get_population(self.last_pop, fits=self.last_fit, grid_flag=True, bounds=self.orig_bounds, bounds_map=self.bounds_map) else: self.history['last_pop'] = get_population(self.last_pop, fits=self.last_fit, grid_flag=False) self.history['F-Evals'] = self.FES #function evaluations if self.verbose: print('------------------------ EDEV Summary --------------------------') print('Best fitness (y) found:', self.fitness_best_correct) print('Best individual (x) found:', self.ind_correct) print('--------------------------------------------------------------') return self.ind_correct, self.fitness_best_correct, self.history