''' -------------------------------------------------------------- Custom PSO algorithm for continuous domains -------------------------------------------------------------- Autor: Rodrigo Araya Email: raaraya1@uandes.cl ''' import math import numpy as np import pandas as pd from F_newton import F_newton from F_coulomb import F_coulomb import plotly.graph_objects as go import plotly.express as px def Random_(problem, x_, auto_reduce_search_space=False): """ This solver generates a random solution for each particle. Additionally, it is also possible to apply an exploitation method where the search space is reduced on each iteration. Args: problem (dic): Dictionary that include: objective function, lower bound and upper bound of each variable and optimal solution x_ (array(size=())): Current position of each particle auto_reduce_search_space (bool, optional): If True, it update the lower and upper bounds in a way to reduce the search space. Defaults to False. Returns: Stemp, S_f: New positions for each particle and their performance after being evaluated with the objective function. """ nPart = x_.shape[0] nVar = x_.shape[1] if auto_reduce_search_space: Lo = np.min(x_, axis=0) Up = np.max(x_, axis=0) else: Lo = np.zeros(nVar) Up = np.ones(nVar) new_x = np.random.uniform(low=Lo, high=Up, size=(nPart, nVar)) Stemp = np.zeros((nPart,nVar)) for k in range(nPart): for i in range(nVar): Stemp[k][i] = new_x[k][i] if Stemp[k][i] > Up[i]: Stemp[k][i] = Up[i] elif Stemp[k][i] < Lo[i]: Stemp[k][i] = Lo[i] f,S_r,maximize = mp_evaluator(problem, Stemp) S_f = np.zeros((nPart,1)) for i in range(len(S_r)): S_f[i] = f[i] return Stemp, S_f def F_newton_(problem, x_, m, Lo, Up, G=0.00001, keep_best=True, weight_method='Linear', t=1.0, max_mass=100.0): _, _, new_x = F_newton(x_, m, minimize=True, G=G, keep_best=keep_best, weight_method=weight_method, t=t, max_mass=max_mass) nPart=x_.shape[0] nVar=x_.shape[1] Stemp = np.zeros((nPart,nVar)) for k in range(nPart): for i in range(nVar): Stemp[k][i] = new_x[k][i] if Stemp[k][i] > Up[i]: Stemp[k][i] = Up[i] elif Stemp[k][i] < Lo[i]: Stemp[k][i] = Lo[i] f,S_r,maximize = mp_evaluator(problem, Stemp) S_f = np.zeros((nPart,1)) for i in range(len(S_r)): S_f[i] = f[i] return Stemp, S_f def F_coulomb_(problem, x_, v, q, P, Lo, Up, k=0.00001, weight_method='Linear', t=1.0, max_q=100.0): _, _, v, new_x = F_coulomb(x_, v, q, P=P, minimize=True, k=k, weight_method=weight_method, t=t, max_q=max_q) nPart=x_.shape[0] nVar=x_.shape[1] Stemp = np.zeros((nPart,nVar)) for k in range(nPart): for i in range(nVar): Stemp[k][i] = new_x[k][i] if Stemp[k][i] > Up[i]: Stemp[k][i] = Up[i] elif Stemp[k][i] < Lo[i]: Stemp[k][i] = Lo[i] f,S_r,maximize = mp_evaluator(problem, Stemp) S_f = np.zeros((nPart,1)) for i in range(len(S_r)): S_f[i] = f[i] return Stemp, v, S_f def evaluator(problem, x): # Dado que se trabaja bajo el dominio [0, 1] para cada variable, se debe crear una funcion que regrese a los valores a los dominios originales # Ejemplo (ndim=2): si se tiene el dominio original [-5, 5] y [-2, 2] para la variables x1, x2 y se cambio a [0, 1] y [0, 1] se vuelve al original tras: # y1=ax+b -> -5 = a*0+b -> b=-5 # y1=ax+b -> 5 = a*1+-5 -> a=10 # y1=10*x+-5 # y2=ax+b -> -2 = a*0+b -> b=-2 # y2=ax+b -> 2 = a*1+-2 -> a=4 # y2=4*x+-2 # Luego se aplica y1(x1) e y2(x2) respectivamente. Extendible a n dimensiones. # Dado que [0, 1] no cambia, se generaliza la formula b=lb y a=ub-lb -> y_{i} = (ub-lb)_{i}*x + lb_{i} lb = [var[0] for var in problem['bounds']] ub = [var[1] for var in problem['bounds']] x = [(ub[ind]-lb[ind])*i+lb[ind] for ind, i in enumerate(x)] # calculate fitness f = problem['f'](x) fitness = dict(Obj=f) return fitness def mp_evaluator(problem, x): results = [evaluator(problem, c) for c in x] f = [r['Obj'] for r in results] # maximization or minimization problem maximize = False return (f, [r for r in results],maximize) def correct_x(problem, x_): lb = [var[0] for var in problem['bounds']] ub = [var[1] for var in problem['bounds']] x = np.empty(x_.shape) for ind, row in enumerate(x_): corr_row = np.array([(ub[ind]-lb[ind])*i+lb[ind] for ind, i in enumerate(row)]) x[ind]=corr_row return x def custom_pso(problem, nPart, nVar, maxiter, G=0.00001, k=0.0001, keep_best=True, weight_method='Linear', t=1.0, seed=0, max_mass=100.0, solver='Newton', P=3, max_q=100.0, auto_reduce_search_space=False, dinamic=False): """The procedure of this algorithm is as follows: 1. Generate a file of size nSize=nPart. 2. Generate nPart initial solutions. Each solution contains nVar variables. 3. Evaluate the nPart solutions and add them to the file ordered from best to worst solution. 4. While the termination condition is not met (iterations > maxiter): 4.1 Generate new solutions using some solver. Options = ['Random', 'Newton', 'Coulomb'] 4.2 Add new solutions to the file. 4.3 Remove duplicate solutions. 4.4 Evaluate solutions and sort from best to worst solution. 4.5 Keep in the file the nPar best solutions. 4.6 Save iteration results in a history (dataframe). 4.7 Evaluate termination condition. If negative, return to step 4.1. Args: problem (dic): Dictionary that include: objective function, lower bound and upper bound of each variable and optimal solution nPart (_type_): Quantity of particles nVar (_type_): Quantity of variables maxiter (_type_): Maximum number of iterations. seed (int, optional): set the generation of random numbers. Defaults to 0. solver (str, optional): solver to apply. Defaults to 'Newton'. Options=['Random', 'Newton', 'Coulomb']. Random Solver: auto_reduce_search_space (bool, optional): If True, it update the lower and upper bounds in a way to reduce the search space. Defaults to False. Newton Solver: G (float, optional): Gravitational constant. Defaults to 0.00001. keep_best (bool, optional): It will keep the best value obtained in each iteration. Defaults to True. weight_method (str, optional): method to reassign particle mass. Defaults to 'Linear'. Options=['Linear', 'Quadratic', 'Exponential']. t (float, optional): time. Defaults to 1.0. max_mass (float, optional): upper bound of mass to assing. Defaults to 100.0. dinamic (bool, optional): It will change the max_mass value depending on the current iteration and difference between best and worst value obteined. Defaults to False. Coulomb Solver: P (int): quantity of electric fields (the best P particles become electric fields) max_q (float, optional): upper bound of electric charge to assing. Defaults to 100.0. weight_method (str, optional): method to reassign electric charges. Defaults to 'Linear'. Options=['Linear', 'Quadratic', 'Exponential']. k (float, optional): electric constant. Defaults to 0.0001. t (float, optional): time. Defaults to 1.0. dinamic (bool, optional): It will change the max_q value depending on the current iteration and difference between best and worst value obteined. Defaults to False. Returns: df, best_var, best_sol: dataframe contening data from all iterations, the best variables and the best value obtained """ # number of variables parameters_v = [f'x{i+1}' for i in range(nVar)] # number of variables nVar = len(parameters_v) # size of solution archive nSize = nPart # number of Particules nPart = nPart # maximum iterations maxiter = maxiter # bounds of variables Up = [1]*nVar Lo = [0]*nVar # dinamic q din_max_q=max_q # dinamic mass din_max_mass = max_mass # initilize matrices S = np.zeros((nSize,nVar)) S_f = np.zeros((nSize,1)) # initial velocity v = np.zeros((nSize,nVar)) # history columns_ = ['iter'] for i in parameters_v: columns_.append(i) columns_.append('f') df = pd.DataFrame(columns=columns_) # generate first random solution #np.random.seed(seed) Srand = np.random.uniform(low=0,high=1,size=(nPart, nVar)) f,S_r,maximize = mp_evaluator(problem, Srand) for i in range(len(S_r)): S_f[i] = f[i] # add responses and "fitness" column to solution S = np.hstack((Srand, v, S_f)) # sort according to fitness (last column) S = sorted(S, key=lambda row: row[-1],reverse = maximize) S = np.array(S) # save each iteration iter_n = np.full((nPart, 1), 0.0) x_ = S[:, 0:nVar] x = correct_x(problem, x_) res = np.reshape(S[:, -1], (nPart, 1)) rows=np.hstack((iter_n, x, res)) df_new = pd.DataFrame(rows, columns=columns_) df = pd.concat([df, df_new]) iterations=1 # iterations while True: # get a new solution if solver == "Random": Stemp, S_f = Random_(problem, S[:, :nVar], auto_reduce_search_space=auto_reduce_search_space) elif solver=="Newton": din_mass=0 if dinamic: din_mass = ((maxiter-iterations)/(maxiter))*(np.max(S_f)-np.min(S_f)) else: din_mass=max_mass m = np.reshape(S[:, -1], (nPart, 1)) Stemp, S_f = F_newton_(problem, S[:, :nVar], m, Lo, Up, G=G, keep_best=keep_best, weight_method=weight_method, t=t, max_mass=din_mass) elif solver == "Coulomb": din_q=0 if dinamic: din_q = ((maxiter-iterations)/(maxiter))*(np.max(S_f)-np.min(S_f)) else: din_q=max_q q = np.reshape(S[:, -1], (nPart, 1)) Stemp, v, S_f = F_coulomb_(problem, S[:, :nVar], v, q, P, Lo, Up, k=k, weight_method=weight_method, t=t, max_q=din_q) # add responses and "fitness" column to solution Ssample = np.hstack((Stemp, v, S_f)) # add new solutions in the solutions table Solution_temp = np.vstack((S,Ssample)) # delate duplicated rows Solution_temp = np.unique(Solution_temp, axis=0) # sort according to "fitness" Solution_temp = sorted(Solution_temp, key=lambda row: row[-1],reverse = maximize) Solution_temp = np.array(Solution_temp) # keep best solutions S = Solution_temp[:nSize][:] # save each iteration iter_n = np.full((nPart, 1), iterations) x_ = S[:, 0:nVar] x = correct_x(problem, x_) res = np.reshape(S[:, -1], (nPart, 1)) rows=np.hstack((iter_n, x, res)) df_new = pd.DataFrame(rows, columns=columns_) df = pd.concat([df, df_new]) iterations += 1 if iterations > maxiter: break best_sol = np.min(df['f']) ind_best_sol = np.argmin(df['f']) best_var = df.iloc[ind_best_sol, 1:len(parameters_v)+1] return df, best_var, best_sol # Test Functions. # Adapted from "https://www.sfu.ca/~ssurjano/optimization.html" def Ackley(x, a=20.0, b=0.2, c=2.0*np.pi): d = len(x) sum1 = np.sum(np.square(x)) sum2 = np.sum(np.array([np.cos(c*i) for i in x])) term1 = -a * np.exp(-b * np.sqrt(sum1 / d)) term2 = -np.exp(sum2 / d) return term1 + term2 + a + np.exp(1) def sixth_Bukin(x): return 100 * np.sqrt(np.abs(x[1] - 0.01*x[0]**2)) + 0.01*np.abs(x[0] + 10) def Cross_in_Tray(x): return -0.0001 * math.pow(np.abs(np.sin(x[0]) * np.sin(x[1]) * np.exp(np.abs(100.0 - np.sqrt(x[0]**2 + x[1]**2)/np.pi)))+1.0, 0.1) def Drop_Wave(x): return -1.0*(1.0 + np.cos(12.0 * np.sqrt(x[0]**2 + x[1]**2))) / (0.5 * (x[0]**2 + x[1]**2) + 2.0) def Eggholder(x): return -(x[1] + 47.0) * np.sin(np.sqrt(np.abs(x[1] + x[0]/2 + 47.0))) - x[0] * np.sin(np.sqrt(np.abs(x[0] - (x[1] + 47.0)))) def Griewank(x): sum_part=0.0 prod_part=1.0 for i, xi in enumerate(x): sum_part += xi/4000.0 prod_part *= np.cos(xi/np.sqrt(i+1)) return sum_part - prod_part + 1.0 def Holder_Table(x): return -np.abs(np.sin(x[0])*np.cos(x[1])*np.exp(np.abs(1.0-(np.sqrt(x[0]**2 + x[1]**2)/np.pi)))) def Levy(x): # d dimensions w1 = 1.0 + (x[0]-1.0)/4.0 wd = 1.0 + (x[-1]-1.0)/4.0 sum_part = 0.0 for i, xi in enumerate(x): wi = 1.0 + (xi-1.0)/4.0 sum_part += ((wi-1.0)**2)*(1.0 + 10.0*math.pow(np.sin(np.pi*wi+1.0), 2)) return math.pow(np.sin(np.pi*w1), 2) + sum_part + ((wd-1.0)**2)*(1.0 + math.pow(np.sin(2*np.pi*wd), 2)) def Rastrigin(x): # d dimensions d = len(x) sum_part = 0.0 for i, xi in enumerate(x): sum_part += (xi**2) - 10.0*np.cos(2*np.pi*xi) return 10*d + sum_part def second_Schaffe(x): return 0.5 + (math.pow(np.sin(x[0]**2 + x[1]**2), 2)-0.5)/(1.0 + 0.001*(x[0]**2 + x[1]**2))**2 def fourth_Schaffer(x): return 0.5 + (math.pow(np.cos(np.abs(x[0]**2 - x[1]**2)), 2)-0.5)/(1.0 + 0.001*(x[0]**2 + x[1]**2))**2 def Schwefel(x): # d dimensions d = len(x) sum_part = 0.0 for xi in x: sum_part += xi*np.sin(np.sqrt(np.abs(xi))) return 418.9829*d - sum_part def Shubert(x): sum_1 = 0.0 sum_2 = 0.0 for i in np.arange(5): i = float(i + 1) sum_1 += i*np.cos((i+1)*x[0] + i) sum_2 += i*np.cos((i+1)*x[1] + i) return sum_1*sum_2 def Styblinski_Tang(x): f = (sum([math.pow(i,4)-16*math.pow(i,2)+5*i for i in x])/2) return f def Easom(x): f = np.cos(x[0])*np.cos(x[1])*np.exp(-(math.pow(x[0]-np.pi, 2) + math.pow(x[1]-np.pi, 2))) return f def Bohachevsky(x): f = x[0]**2 + 2*x[1]**2 -0.3*np.cos(3*np.pi*x[0]) - 0.4*np.cos(4*np.pi*x[1]) + 0.7 return f def Perm_d_beta(x, d=2, beta=4): f = 0.0 for i in range(d): for j in range(d): f += ((j+1) + beta)*(x[j]**(i+1) - (1/(j+1)**(i+1)))**2 return f def Rotated_Hyper_Ellipsoid(x, d=2): f = 0.0 for i in range(d): for j in range(i+1): f += x[j]**2 return f def Sum_of_Different_Powers(x, d=2): f = 0.0 for i in range(d): f += np.abs(x[i])**i+1+1 return f def SUM_SQUARES(x, d=2): f = 0.0 for i in range(d): f += (i+1)*x[i]**2 return f def TRID(x, d=2): sum_1 = 0.0 sum_2 = 0.0 for i in range(d): sum_1 += (x[i] - 1)**2 for i in range(d-1): sum_2 += x[i+1]*x[i] f = sum_1 + sum_2 return f def BOOTH(x): f = (x[0] + 2*x[1] -7)**2 + (2*x[0] + x[1] - 5)**2 return f def Matyas(x): f = 0.26*(x[0]**2 + x[1]**2) - 0.48*x[0]*x[1] return f def MCCORMICK(x): f = np.sin(x[0] + x[1]) + (x[0] - x[1])**2 - 1.5*x[0] + 2.5*x[1] + 1 return f def Power_Sum(x, d=2, b=[8, 18, 44, 114]): f = 0.0 for i in range(d): sum_1 = 0.0 for j in range(d): sum_1 += x[j]**(i+1) f += (sum_1 - b[i])**2 return f def Zakharov(x, d=2): f = 0.0 sum_1 = 0.0 sum_2 = 0.0 sum_3 = 0.0 for i in range(d): sum_1 += x[i]**2 sum_2 += 0.5*(i+1)*x[i] sum_3 += 0.5*(i+1)*x[i] f = sum_1 + sum_2**2 + sum_3**4 return f def THREE_HUMP_CAMEL(x): f = 2*x[0]**2 - 1.05*x[0]**4 + (x[0]**6/6) + x[0]*x[1] + x[1]**2 return f def SIX_HUMP_CAMEL(x): f = (4 - 2.1*x[0] + (x[0]**4)/3)*x[0]**2 + x[0]*x[1] + (-4 + 4*x[1]**2)*x[1]**2 return f def DIXON_PRICE(x, d=2): sum_1 = 0.0 for i in range(d-1): i = i + 1 sum_1 += (i+1)*(2*x[i]**2 - x[i-1])**2 f = (x[0] - 1)**2 + sum_1 return f def ROSENBROCK(x, d=2): f = 0.0 for i in range(d-1): f += 100*(x[i+1] - x[i]**2)**2 + (x[i] - 1)**2 return f def DE_JONG(x): a = [[-32, -16, 0, 16, 32, -32, -16, 0, 16, 32, -32, -16, 0, 16, 32, -32, -16, 0, 16, 32, -32, -16, 0, 16, 32], [-32, -32, -32, -32, -32, -16, -16, -16, -16, -16, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32]] sum_1 = 0.0 for i in range(25): sum_1 += 1/((i+1) + (x[0] - a[0][i])**6 + (x[1] - a[1][i])**6) f = (0.002 + sum_1)**(-1) return f def MICHALEWICZ(x, d=2, m=10): f = 0.0 for i in range(d): f += np.sin(x[i])*np.sin(((i+1)*x[i]**2)/np.pi)**(2*m) f = -f return f def BEALE(x): f = (1.5 - x[0] + x[0]*x[1])**2 + (2.25 - x[0] + x[0]*x[1]**2)**2 + (2.625 - x[0] + x[0]*x[1]**3)**2 return f def BRANIN(x): a=1 b=5.1/(4*(np.pi)**2) c = 5/np.pi r = 6 s = 10 t = 1/(8*np.pi) f = a*(x[1] - b*x[0]**2 + c*x[0] - r)**2 + s*(1 - t)*np.cos(x[0]) + s return f def GOLDSTEIN_PRICE(x): f = (1 + ((x[0] + x[1] + 1)**2)*(19 - 14*x[0] + 3*x[0]**2 - 14*x[1] + 6*x[0]*x[1] + 3*x[1]**2))*(30 + ((2*x[0] - 3*x[1])**2)*(18 - 32*x[0] + 12*x[0]**2 + 48*x[1] - 36*x[0]*x[1] + 27*x[1]**2)) return f def PERM_D_BETA(x, d=2, beta=1): f = 0.0 for i in range(d): sum_1 = 0 for j in range(d): sum_1 += ((j+1)**(i+1) + beta)*((x[j]/(j+1))**(i+1) - 1) f += sum_1 return f class test_functions(): def __init__(self) -> None: self.Ackley_ = {'name':'Ackley', 'f':Ackley, 'bounds':[[-32.768, 32.768], [-32.768, 32.768]], 'opt':[[0.0, 0.0], 0.0]} self.sixth_Bukin_ = {'name':'sixth_Bukin', 'f':sixth_Bukin, 'bounds':[[-15.0, -5.0], [-3.0, 3.0]], 'opt':[[-10.0, 1.0], 0.0]} self.Cross_in_Tray_ = {'name':'Cross_in_Tray', 'f':Cross_in_Tray, 'bounds':[[-10.0, 10.0], [-10.0, 10.0]], 'opt':[[[1.3491, -1.3491], [1.3491, 1.3491], [-1.3491, 1.3491], [-1.3491, -1.3491]], -2.06261]} self.Drop_Wave_ = {'name':'Drop_Wave', 'f':Drop_Wave, 'bounds':[[-5.12, 5.12], [-5.12, 5.12]], 'opt':[[0, 0], -1.0]} self.Eggholder_ = {'name':'Eggholder', 'f':Eggholder, 'bounds':[[-512.0, 512.0], [-512.0, 512.0]], 'opt':[[512.404, 512.404], -959.6407]} self.Griewank_ = {'name':'Griewank', 'f':Griewank, 'bounds':[[-600.0, 600.0], [-600.0, 600.0]], 'opt':[[0.0, 0.0], 0.0]} self.Holder_Table_ = {'name':'Holder_Table', 'f':Holder_Table, 'bounds':[[-10.0, 10.0], [-10.0, 10.0]], 'opt':[[[8.05502, 9.66459], [8.05502, -9.66459], [-8.05502, 9.66459], [-8.05502, -9.66459]], -19.2085]} self.Levy_ = {'name':'Levy', 'f':Levy, 'bounds':[[-10.0, 10.0], [-10.0, 10.0]], 'opt':[[1.0, 1.0], 0.0]} self.Rastrigin_ = {'name':'Rastrigin', 'f':Rastrigin, 'bounds':[[-5.12, 5.12], [-5.12, 5.12]], 'opt':[[0.0, 0.0], 0.0]} self.second_Schaffe_ = {'name':'second_Schaffe', 'f':second_Schaffe, 'bounds':[[-100.0, 100.0], [-100.0, 100.0]], 'opt':[[0.0, 0.0], 0.0]} self.fourth_Schaffer_ = {'name':'fourth_Schaffer', 'f':fourth_Schaffer, 'bounds':[[-100.0, 100.0], [-100.0, 100.0]], 'opt':[[0.0, 0.0], 0.0]} self.Schwefel_ = {'name':'Schwefel', 'f':Schwefel, 'bounds':[[-500.0, 500.0], [-500.0, 500.0]], 'opt':[[420.9687, 420.9687], 0.0]} self.Shubert_ = {'name':'Shubert', 'f':Shubert, 'bounds':[[-10.0, 10.0], [-10.0, 10.0]], 'opt':[[0.0, 0.0], -186.7309]} self.Styblinski_Tang_ = {'name':'Styblinski_Tang', 'f':Styblinski_Tang, 'bounds':[[-5, 5], [-5, 5]]} self.Easom_ = {'name':'Easom', 'f':Easom, 'bounds':[[-3, 3], [-3, 3]]} self.Bohachevsky_ = {'name':'Bohachevsky', 'f':Bohachevsky, 'bounds':[[-100.0, 100.0], [-100.0, 100.0]], 'opt':[[0, 0], 0]} self.Perm_d_beta_ = {'name':'Perm_d_beta', 'f':Perm_d_beta, 'bounds':[[-2.0, 2.0], [-2.0, 2.0]], 'opt':[[1, 0.5], 0]} self.Rotated_Hyper_Ellipsoid_ = {'name':'Rotated_Hyper_Ellipsoid', 'f':Rotated_Hyper_Ellipsoid, 'bounds':[[-65.536, 65.536], [-65.536, 65.536]], 'opt':[[0.0, 0.0], 0]} self.Sum_of_Different_Powers_ = {'name':'Sum_of_Different_Powers', 'f':Sum_of_Different_Powers, 'bounds':[[-1, 1], [-1, 1]], 'opt':[[0.0, 0.0], 0]} self.SUM_SQUARES_ = {'name':'SUM_SQUARES', 'f':SUM_SQUARES, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[0.0, 0.0], 0]} self.TRID_ = {'name':'TRID', 'f':TRID, 'bounds':[[-4, 4], [-4, 4]], 'opt':[[2, 2], -2]} self.BOOTH_ = {'name':'BOOTH', 'f':BOOTH, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[1, 3], 0]} self.Matyas_ = {'name':'Matyas', 'f':Matyas, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[0, 0], 0]} self.MCCORMICK_ = {'name':'MCCORMICK', 'f':MCCORMICK, 'bounds':[[-1.5, 4], [-3, 4]], 'opt':[[-0.54719, -1.54719], -1.9133]} self.Power_Sum_ = {'name':'Power_Sum', 'f':Power_Sum, 'bounds':[[0, 2], [0, 2]]} self.Zakharov_ = {'name':'Zakharov', 'f':Zakharov, 'bounds':[[-5, 10], [-5, 10]], 'opt':[[0.0, 0.0], 0.0]} self.THREE_HUMP_CAMEL_ = {'name':'THREE_HUMP_CAMEL', 'f':THREE_HUMP_CAMEL, 'bounds':[[-5, 5], [-5, 5]], 'opt':[[0.0, 0.0], 0.0]} self.SIX_HUMP_CAMEL_ = {'name':'SIX_HUMP_CAMEL', 'f':SIX_HUMP_CAMEL, 'bounds':[[-3, 3], [-2, 2]], 'opt':[[0.0898, -0.7126], -1.0316]} self.DIXON_PRICE_ = {'name':'DIXON_PRICE', 'f':DIXON_PRICE, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[1, 1/np.sqrt(2)], 0]} self.ROSENBROCK_ = {'name':'ROSENBROCK', 'f':ROSENBROCK, 'bounds':[[-5, 10], [-5, 10]], 'opt':[[1, 1], 0]} self.DE_JONG_ = {'name':'DE_JONG', 'f':DE_JONG, 'bounds':[[-65.536, 65.536], [-65.536, 65.536]]} self.MICHALEWICZ_ = {'name':'MICHALEWICZ', 'f':MICHALEWICZ, 'bounds':[[0, np.pi], [0, np.pi]], 'opt':[[2.2, 1.57], -1.8013]} self.BEALE_ = {'name':'BEALE', 'f':BEALE, 'bounds':[[-4.5, 4.5], [-4.5, 4.5]], 'opt':[[3, 0.5], 0]} self.BRANIN_ = {'name':'BRANIN', 'f':BRANIN, 'bounds':[[-5, 10], [0, 15]], 'opt':[[-np.pi, 12.275], 0.397887]} self.GOLDSTEIN_PRICE_ = {'name':'GOLDSTEIN_PRICE', 'f':GOLDSTEIN_PRICE, 'bounds':[[-2, 2], [-2, 2]], 'opt':[[0, -1], 3]} self.PERM_D_BETA_ = {'name':'PERM_D_BETA', 'f':PERM_D_BETA, 'bounds':[[-2, 2], [-2, 2]], 'opt':[[1, 2], 0]} self.dictionary = {'Ackley': self.Ackley_, 'sixth_Bukin':self.sixth_Bukin_, 'Cross_in_Tray':self.Cross_in_Tray_, 'Drop_Wave':self.Drop_Wave_, 'Eggholder':self.Eggholder_, 'Griewank':self.Griewank_, 'Holder_Table':self.Holder_Table_, 'Levy':self.Levy_, 'Rastrigin':self.Rastrigin_, 'second_Schaffe':self.second_Schaffe_, 'fourth_Schaffer':self.fourth_Schaffer_, 'Schwefel':self.Schwefel_, 'Shubert':self.Shubert_, 'Styblinski_Tang':self.Styblinski_Tang_, 'Easom':self.Easom_, 'Bohachevsky':self.Bohachevsky_, 'Perm_d_beta':self.Perm_d_beta_, 'Rotated_Hyper_Ellipsoid':self.Rotated_Hyper_Ellipsoid_, 'Sum_of_Different_Powers': self.Sum_of_Different_Powers_, 'SUM_SQUARES':self.SUM_SQUARES_, 'TRID':self.TRID_, 'BOOTH': self.BOOTH_, 'Matyas':self.Matyas_, 'MCCORMICK': self.MCCORMICK_, 'Power_Sum':self.Power_Sum_, 'Zakharov':self.Zakharov_, 'THREE_HUMP_CAMEL' :self.THREE_HUMP_CAMEL_, 'SIX_HUMP_CAMEL': self.SIX_HUMP_CAMEL_, 'DIXON_PRICE': self.DIXON_PRICE_, 'ROSENBROCK_': self.ROSENBROCK_, 'DE_JONG':self.DE_JONG_, 'MICHALEWICZ': self.MICHALEWICZ_, 'BEALE':self.BEALE_, 'BRANIN': self.BRANIN_, 'GOLDSTEIN_PRICE':self.GOLDSTEIN_PRICE_, 'PERM_D_BETA':self.PERM_D_BETA_} self.whole_list = list(self.dictionary.keys()) def plotly_graph(problem, df=None): function=problem['f'] x_lb=problem['bounds'][0][0] x_ub=problem['bounds'][0][1] y_lb=problem['bounds'][1][0] y_ub=problem['bounds'][1][1] x = np.linspace(x_lb, x_ub, 100) y = np.linspace(y_lb, y_ub, 100) z = np.empty((100, 100)) for ind_y, j in enumerate(y): for ind_x, i in enumerate(x): z[ind_y][ind_x] = function(np.array([i, j])) steps_ = int(np.max(df['iter'])) fig1 = go.Figure(data=[go.Surface(x=x, y=y, z=z)]) for step in range(steps_): points = df[df['iter']==step] points_x = list(points['x1']) points_y = list(points['x2']) points_z = list(points['f']) fig1.add_scatter3d(x=np.array(points_x), y=np.array(points_y), z=np.array(points_z), mode='markers', visible=False, marker=dict(size=5, color="white", line=dict(width=1, color="black"))) fig1.update_layout(title=f"f = {step}") # Create figure fig = go.Figure(data=[go.Scatter3d(x=[], y=[], z=[], mode="markers", marker=dict(size=5, color="white", line=dict(width=1, color="black")) ), fig1.data[0]] ) # Frames frames = [go.Frame(data=[go.Scatter3d(x=k['x'], y=k['y'], z=k['z'] ), fig1.data[0] ], traces= [0], name=f'frame{ind}' ) for ind, k in enumerate(fig1.data[1:]) ] fig.update(frames=frames) def frame_args(duration): return { "frame": {"duration": duration}, "mode": "immediate", "fromcurrent": True, "transition": {"duration": duration, "easing": "linear"}, } sliders = [ {"pad": {"b": 10, "t": 60}, "len": 0.9, "x": 0.1, "y": 0, "steps": [ {"args": [[f.name], frame_args(0)], "label": str(k), "method": "animate", } for k, f in enumerate(fig.frames) ] } ] fig.update_layout( updatemenus = [{"buttons":[ { "args": [None, frame_args(150)], "label": "Play", "method": "animate", }, { "args": [[None], frame_args(150)], "label": "Pause", "method": "animate", }], "direction": "left", "pad": {"r": 10, "t": 70}, "type": "buttons", "x": 0.1, "y": 0, } ], sliders=sliders ) fig.update_layout(sliders=sliders) fig.write_html('animation.html') return fig problem=test_functions().PERM_D_BETA_ df, best_var, best_sol = custom_pso(problem, 20, 2, maxiter=100, solver="Newton", k=0.000000001, G=0.00000001, t=1.0, max_q=0.01, auto_reduce_search_space=True, dinamic=True) #plotly_graph(problem, df) print(best_sol)