|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +""" |
| 3 | +Created on Sun Apr 12 18:04:30 2020 |
| 4 | +
|
| 5 | +@author: Basharat |
| 6 | +
|
| 7 | +#!/usr/bin/env python3 |
| 8 | +# -*- coding: utf-8 -*- |
| 9 | +
|
| 10 | +""" |
| 11 | + |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | +import numpy as np |
| 16 | +from sko.tools import func_transformer |
| 17 | +# Now Plot the animation |
| 18 | +import matplotlib.pyplot as plt |
| 19 | +from matplotlib.animation import FuncAnimation |
| 20 | + |
| 21 | +# PSO (Particle swarm optimization) algorithm |
| 22 | +class PSO(): |
| 23 | + """ |
| 24 | + Parameters |
| 25 | + -------------------- |
| 26 | + cost_func : function |
| 27 | + The func you want to do optimal |
| 28 | + dimension : int |
| 29 | + Number of dimension, which is number of parameters of func. |
| 30 | + N : int |
| 31 | + Size of population, which is the number of Particles. |
| 32 | + max_iteration : int |
| 33 | + Max no of iterations |
| 34 | +
|
| 35 | + Attributes |
| 36 | + ---------------------- |
| 37 | + pbest_x : array_like, shape is (N,dim) |
| 38 | + best location of every particle in history |
| 39 | + pbest_y : array_like, shape is (N,1) |
| 40 | + best image of every particle in history |
| 41 | + gbest_x : array_like, shape is (1,dim) |
| 42 | + general best location for all particles in history |
| 43 | + gbest_y : float |
| 44 | + general best image for all particles in history |
| 45 | + gbest_y_hist : list |
| 46 | + gbest_y of every iteration |
| 47 | + """ |
| 48 | + |
| 49 | + def __init__(self, cost_func, dimension, population_size=40, max_iteration=150, lb=None, ub=None, w=0.8, c1=0.5, c2=0.5): |
| 50 | + self.func = func_transformer(cost_func) |
| 51 | + |
| 52 | + self.w = w # inertia weight |
| 53 | + self.c1, self.c2 = c1, c2 # parameters to control personal best, global best respectively |
| 54 | + self.N = population_size # number of particles |
| 55 | + self.dimension = dimension # dimension of particles, which is the number of variables of func |
| 56 | + self.max_iter = max_iteration # max iter |
| 57 | + |
| 58 | + self.has_constraints = not (lb is None and ub is None) |
| 59 | + self.lb = -np.ones(self.dimension) if lb is None else np.array(lb) |
| 60 | + self.ub = np.ones(self.dimension) if ub is None else np.array(ub) |
| 61 | + v_high = self.ub - self.lb |
| 62 | + |
| 63 | + assert self.dimension == len(self.lb) == len(self.ub), 'dimension == len(lb) == len(ub) is not True' |
| 64 | + assert np.all(self.ub > self.lb), 'upper-bound must be greater than lower-bound' |
| 65 | + |
| 66 | + self.X = np.random.uniform(low=self.lb, high=self.ub, size=(self.N, self.dimension)) # position of particles |
| 67 | + self.V = np.random.uniform(low=-v_high, high=v_high, size=(self.N, self.dimension)) # speed of particles |
| 68 | + self.Y = self.calculate_objfunc_Y() # y = f(x) for all particles |
| 69 | + |
| 70 | + self.pbest_x = self.X.copy() # personal best location of every particle in history |
| 71 | + self.pbest_y = self.Y.copy() # best objective cost of every particle in history |
| 72 | + |
| 73 | + self.gbest_x = np.zeros((1, self.dimension)) # global best location for all particles |
| 74 | + self.gbest_y = np.inf # global best objective cost for all particles |
| 75 | + |
| 76 | + self.gbest_y_hist = [] # gbest_y of every iteration |
| 77 | + self.update_gbest() |
| 78 | + |
| 79 | + # record verbose values |
| 80 | + self.record_mode = False |
| 81 | + self.record_value = {'X': [], 'V': [], 'Y': []} |
| 82 | + |
| 83 | + # Update the velocity to get new value |
| 84 | + def update_V(self): |
| 85 | + r1 = np.random.rand(self.N, self.dimension) |
| 86 | + r2 = np.random.rand(self.N, self.dimension) |
| 87 | + |
| 88 | + self.V = self.w * self.V + \ |
| 89 | + self.c1 * r1 * (self.pbest_x - self.X) + \ |
| 90 | + self.c2 * r2 * (self.gbest_x - self.X) |
| 91 | + |
| 92 | + # Update the position to get new value |
| 93 | + def update_X(self): |
| 94 | + self.X = self.X + self.V |
| 95 | + |
| 96 | + if self.has_constraints: |
| 97 | + self.X = np.clip(self.X, self.lb, self.ub) |
| 98 | + |
| 99 | + # Calculate objective function of all particles |
| 100 | + def calculate_objfunc_Y(self): |
| 101 | + # calculate y for every x in X |
| 102 | + self.Y = self.func(self.X).reshape(-1, 1) |
| 103 | + return self.Y |
| 104 | + |
| 105 | + # Local best position of all the particles |
| 106 | + def update_pbest(self): |
| 107 | + ''' |
| 108 | + personal best |
| 109 | + :return: |
| 110 | + ''' |
| 111 | + self.pbest_x = np.where(self.pbest_y > self.Y, self.X, self.pbest_x) |
| 112 | + self.pbest_y = np.where(self.pbest_y > self.Y, self.Y, self.pbest_y) |
| 113 | + |
| 114 | + # Global best position of all the particles |
| 115 | + def update_gbest(self): |
| 116 | + ''' |
| 117 | + global best |
| 118 | + :return: |
| 119 | + ''' |
| 120 | + if self.gbest_y > self.Y.min(): |
| 121 | + self.gbest_x = self.X[self.Y.argmin(), :].copy() |
| 122 | + self.gbest_y = self.Y.min() |
| 123 | + |
| 124 | + # Track values by iteration to print later |
| 125 | + def recorder(self): |
| 126 | + if not self.record_mode: |
| 127 | + return |
| 128 | + self.record_value['X'].append(self.X) |
| 129 | + self.record_value['V'].append(self.V) |
| 130 | + self.record_value['Y'].append(self.Y) |
| 131 | + |
| 132 | + # Run PSO algorithm |
| 133 | + def run(self, max_iter=None): |
| 134 | + self.max_iter = max_iter or self.max_iter |
| 135 | + for iter_num in range(self.max_iter): |
| 136 | + self.update_V() |
| 137 | + self.recorder() |
| 138 | + self.update_X() |
| 139 | + self.calculate_objfunc_Y() |
| 140 | + self.update_pbest() |
| 141 | + self.update_gbest() |
| 142 | + |
| 143 | + self.gbest_y_hist.append(self.gbest_y) |
| 144 | + return self |
| 145 | + |
| 146 | + fit = run |
| 147 | + |
| 148 | + |
| 149 | +#--- COST FUNCTIONS ------------------------------------------------------------+ |
| 150 | +# function we are attempting to optimize (minimize) |
| 151 | + |
| 152 | +def costFunc0(x): |
| 153 | + [x1, x2] = x |
| 154 | + return x1 ** 2 + (x2 - 0.05) ** 2 |
| 155 | + |
| 156 | +def costFunc1(x): |
| 157 | + total=0 |
| 158 | + for i in range(len(x)): |
| 159 | + total+=x[i]**2 |
| 160 | + return total |
| 161 | + |
| 162 | +## https://www.researchgate.net/publication/261197555_A_generic_particle_swarm_optimization_Matlab_function |
| 163 | +# function we are attempting to optimize (minimize) |
| 164 | +def costFunc2(x): |
| 165 | + n = len(x) |
| 166 | + sum_1=0 |
| 167 | + for i in range(len(x)): |
| 168 | + sum_1+=x[i]**2 |
| 169 | + |
| 170 | + sum_2=0 |
| 171 | + for i in range(len(x)): |
| 172 | + sum_2+= np.cos(2*np.pi*x[i]) |
| 173 | + |
| 174 | + f = 20 + np.exp(1) - 20*np.exp(-0.2*np.sqrt((1/n)*sum_1)) - np.exp((1/n)*sum_2); |
| 175 | + return f |
| 176 | + |
| 177 | +# https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/september/test-run-multi-swarm-optimization |
| 178 | +def costFunc3(x): |
| 179 | + f=0 |
| 180 | + for i in range(len(x)): |
| 181 | + f += (x[i]**2) - (10 * np.cos(2 * np.pi * x[i])) |
| 182 | + return f + 20 |
| 183 | + |
| 184 | + |
| 185 | +######################################## |
| 186 | +####### CHANGE THE FUNCTION BELOW ###### |
| 187 | +######################################## |
| 188 | +f = costFunc0 |
| 189 | + |
| 190 | +######################################## |
| 191 | +####### MAIN PSO call ###### |
| 192 | +######################################## |
| 193 | +#pso = PSO(func=demo_func, dimension=2, population_size=30, max_iter=100, lb=[-1, -1], ub=[1, 1]) |
| 194 | +pso = PSO(cost_func=f, dimension=2, population_size=30, max_iteration=100, lb=[-1, -1], ub=[1, 1]) |
| 195 | +#pso = PSO(cost_func=f, dimension=2, population_size=3, max_iteration=5, lb=[-1, -1], ub=[1, 1]) |
| 196 | +pso.record_mode = True |
| 197 | +pso.run() |
| 198 | +print('best_x is ', pso.gbest_x, 'best_y is', pso.gbest_y) |
| 199 | + |
| 200 | + |
| 201 | + |
| 202 | + |
| 203 | +######################################## |
| 204 | +####### Plot PSO on 3D surfice ###### |
| 205 | +######################################## |
| 206 | +#================================== |
| 207 | +# Plotting the pso |
| 208 | +fig = plt.figure() |
| 209 | +ax = fig.add_subplot(111, projection='3d') |
| 210 | + |
| 211 | +ax.set_title('title', loc='center') |
| 212 | +line = ax.plot([], [], 'b.') |
| 213 | + |
| 214 | +X_grid, Y_grid = np.meshgrid(np.linspace(-1.0, 1.0, 40), np.linspace(-1.0, 1.0, 40)) |
| 215 | +Z_grid = f((X_grid, Y_grid)) |
| 216 | +ax.plot_surface(X_grid, Y_grid, Z_grid, cmap='jet', linewidth=0, antialiased=True, alpha=0.5) |
| 217 | + |
| 218 | +ax.set_xlim(-1, 1) |
| 219 | +ax.set_ylim(-1, 1) |
| 220 | + |
| 221 | +plt.ion() |
| 222 | +p = plt.show() |
| 223 | + |
| 224 | +def animate(frame): |
| 225 | + i, j = frame // 10, frame % 10 |
| 226 | + ax.set_title('iter = ' + str(i)) |
| 227 | + X_tmp = X_list[i] + V_list[i] * j / 10.0 |
| 228 | + plt.setp(line, 'xdata', X_tmp[:, 0], 'ydata', X_tmp[:, 1]) |
| 229 | + return line |
| 230 | + |
| 231 | +ani = FuncAnimation(fig, animate, blit=True, interval=40, frames=400) |
| 232 | +plt.show() |
| 233 | + |
| 234 | + |
| 235 | + |
| 236 | +########################################################### |
| 237 | +####### Plot PSO on contour and animate particles ###### |
| 238 | +########################################################### |
| 239 | +record_value = pso.record_value |
| 240 | +X_list, V_list = record_value['X'], record_value['V'] |
| 241 | + |
| 242 | +fig, ax = plt.subplots(1, 1) |
| 243 | + |
| 244 | +# Plotting the pso |
| 245 | +# fig = plt.figure() |
| 246 | +# ax = fig.add_subplot(111, projection='3d') |
| 247 | + |
| 248 | +ax.set_title('title', loc='center') |
| 249 | +line = ax.plot([], [], 'b.') |
| 250 | + |
| 251 | +X_grid, Y_grid = np.meshgrid(np.linspace(-1.0, 1.0, 40), np.linspace(-1.0, 1.0, 40)) |
| 252 | +Z_grid = f((X_grid, Y_grid)) |
| 253 | +ax.contour(X_grid, Y_grid, Z_grid, 10) |
| 254 | + |
| 255 | +ax.set_xlim(-1, 1) |
| 256 | +ax.set_ylim(-1, 1) |
| 257 | + |
| 258 | +plt.ion() |
| 259 | +p = plt.show() |
| 260 | + |
| 261 | + |
| 262 | +def update_scatter(frame): |
| 263 | + i, j = frame // 10, frame % 10 |
| 264 | + ax.set_title('iter = ' + str(i)) |
| 265 | + X_tmp = X_list[i] + V_list[i] * j / 10.0 |
| 266 | + plt.setp(line, 'xdata', X_tmp[:, 0], 'ydata', X_tmp[:, 1]) |
| 267 | + return line |
| 268 | + |
| 269 | + |
| 270 | +ani = FuncAnimation(fig, update_scatter, interval=25, frames=250, repeat=True) |
| 271 | +plt.show() |
| 272 | + |
| 273 | +#ani.save('pso.gif', writer='pillow') |
0 commit comments