├── examples.bmp ├── LICENSE ├── README.md └── Code_Python ├── test.py └── pso.py /examples.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielegilardi/PSO/HEAD/examples.bmp -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Gabriele Gilardi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Metaheuristic Minimization Using Particle Swarm Optimization 2 | 3 | ## Reference 4 | 5 | - Maurice Clerc, 2012, "[Standard Particle Swarm Optimisation](https://hal.archives-ouvertes.fr/hal-00764996/document)", hal-00764996. 6 | - Maurice Clerc, 2006, "[Particle Swarm Optimisation](https://onlinelibrary.wiley.com/doi/book/10.1002/9780470612163)", ISTE Ltd. 7 | - Martin Roberts, 2020, ["How to generate uniformly random points on n-spheres and in n-balls"](http://extremelearning.com.au/how-to-generate-uniformly-random-points-on-n-spheres-and-n-balls/), Extreme Learning. 8 | 9 | ## Characteristics 10 | 11 | - The code has been written and tested in Python 3.7.7. 12 | - Particle Swarm Optimization (PSO) implementation for metaheuristic minimization. 13 | - Variables can be real, integer, or mixed real/integer. 14 | - Variables can be constrained to a specific interval or value setting the lower and the upper boundaries. 15 | - Confidence coefficients depend on one single parameter. 16 | - Search space can be normalized to improve convergency. 17 | - An adaptive random topology is used to define each agent's neighbourhood (with an option to use the full swarm as neighbourhood). 18 | - Unbiased velocity equation using hyperspherical uniform distribution (including the corner case where an agent is the neighbourhood best). 19 | - Three velocity confinement methods (hyperbolic, random-back, and mixed hyperbolic/random-back). 20 | - Possibility to specify the velocity limits. 21 | - Possibility to specify an initial position for the agents. 22 | - To improve the execution speed the algorithm has been designed without any loop on the agents. 23 | - An arbitrary number of parameters can be passed (in a tuple) to the function to minimize. 24 | - Option to run sequential tests with constant or random (uniformly distributed) number of agents. 25 | - Usage: *python test.py example*. 26 | 27 | ## Parameters 28 | 29 | `example` Name of the example to run (Parabola, Alpine, Tripod, and Ackley.) 30 | 31 | `func` Function to minimize. The position of all agents is passed to the function at the same time. 32 | 33 | `LB`, `UB` Lower and upper boundaries of the search space. 34 | 35 | `nPop`, `epochs` Number of agents (population) and number of iterations. 36 | 37 | `K` Average size of each agent's group of informants. If `K=0` the entire swarm is used as agent's group of informants. 38 | 39 | `phi` Coefficient to calculate the self-confidence coefficient and the confidence-in-others coefficient. 40 | 41 | `vel_fact` Velocity factor to calculate the maximum and the minimum allowed velocities. 42 | 43 | `conf_type` Confinement type (on the velocities): `HY=` hyperbolic, `RB=` random-back, `MX=` mixed hyeperbolic/random-back. 44 | 45 | `IntVar` List of indexes specifying which variable should be treated as integer. If all variables are real set `IntVar=None`, if all variables are integer set `IntVar=all`. Indexes are in the range `(1,nVar)`. 46 | 47 | `normalize` Specifies if the search space should be normalized (to improve convergency). 48 | 49 | `rad` Normalized radius of the hypersphere centered on the best particle. The higher the number of other particles inside and the better is the solution. 50 | 51 | `args` Tuple containing any parameter that needs to be passed to the function to minimize. If no parameters are passed set `args=None`. 52 | 53 | `Xinit` Initial position of each agent. 54 | 55 | `nVar` Number of variables (dimensions of the search space). 56 | 57 | `nRun` Number of runs for a specific case. 58 | 59 | `random_pop` Specifies if the number of agents (for each run) is kept constant at `nPop` or randomly choosen from the uniform distribution `nVar/2 <= nPop <= nVar/2`. 60 | 61 | `Xsol` Solution to the minimization point. Set to `None` if not known. 62 | 63 | ## Examples 64 | 65 | There are four examples: Parabola, Alpine, Tripod, and Ackley (see *test.py* for the specific equations and parameters). As illustration, a 3D plot of these function is shown [here](examples.bmp). 66 | 67 | - **Parabola**, **Alpine**, and **Ackley** can have an arbitrary number of dimensions, while **Tripod** has only two dimensions. 68 | 69 | - **Parabola**, **Tripod**, and **Ackley** are examples where parameters (respectively, array `X0`, scalars `kx` and `ky`, and array `X0`) are passed using `args`. 70 | 71 | - The global minimum for **Parabola** and **Ackley** is at `X0`; the global minimum for **Alpine** is at zero; the global minimum for **Tripod** is at `[0,-ky]` with local minimum at `[-kx,+ky]` and `[+kx,+ky]`. 72 | -------------------------------------------------------------------------------- /Code_Python/test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Metaheuristic Minimization Using Particle Swarm Optimization. 3 | 4 | Copyright (c) 2020 Gabriele Gilardi 5 | 6 | References 7 | ---------- 8 | - Maurice Clerc, 2012, "Standard Particle Swarm Optimisation", hal-00764996, 9 | at https://hal.archives-ouvertes.fr/hal-00764996/document. 10 | - Maurice Clerc, 2006, "Particle Swarm Optimisation", ISTE Ltd, at 11 | https://onlinelibrary.wiley.com/doi/book/10.1002/9780470612163. 12 | - Martin Roberts, 2020, "How to generate uniformly random points on n-spheres 13 | and in n-balls", Extreme Learning, at http://extremelearning.com.au 14 | 15 | Characteristics 16 | --------------- 17 | - The code has been written and tested in Python 3.7.7. 18 | - Particle Swarm Optimization (PSO) implementation for metaheuristic 19 | minimization. 20 | - Variables can be real, integer, or mixed real/integer. 21 | - Confidence coefficients depend on one single parameter. 22 | - Search space can be normalized to improve convergency. 23 | - An adaptive random topology is used to define each agent's neighbourhood 24 | (with an option to use the full swarm as neighbourhood). 25 | - Unbiased velocity equation using hyperspherical uniform distribution 26 | (including the corner case where an agent is the neighbourhood best). 27 | - Three velocity confinement methods (hyperbolic, random-back, and mixed 28 | hyperbolic/random-back). 29 | - Possibility to specify the velocity limits. 30 | - Possibility to specify an initial position for the agents. 31 | - To improve the execution speed the algorithm has been designed without 32 | any loop on the agents. 33 | - An arbitrary number of parameters can be passed (in a tuple) to the 34 | function to minimize. 35 | - Option to run sequential tests with constant or random (uniformly 36 | distributed) number of agents. 37 | - Usage: python test.py . 38 | 39 | Parameters 40 | ---------- 41 | example 42 | Name of the example to run (Parabola, Alpine, Tripod, and Ackley.) 43 | func 44 | Function to minimize. The position of all agents is passed to the function 45 | at the same time. 46 | LB, UB 47 | Lower and upper boundaries of the search space. 48 | nPop >=1, epochs >= 1 49 | Number of agents (population) and number of iterations. 50 | K >= 0 51 | Average size of each agent's group of informants. If K=0 the entire swarm 52 | is used as agent's group of informants. 53 | phi >= 2 54 | Coefficient to calculate the self-confidence and the confidence-in-others 55 | coefficients. 56 | vel_fact > 0 57 | Velocity factor to calculate the max. and min. allowed velocities. 58 | conf_type = HY, RB, MX 59 | Confinement on the velocities: hyperbolic, random-back, mixed. 60 | IntVar 61 | List of indexes specifying which variable should be treated as integer. 62 | If all variables are real set IntVar=None, if all variables are integer 63 | set IntVar='all'. Indexes are in the range (1,nVar). 64 | normalize = True, False 65 | Specifies if the search space should be normalized. 66 | 0 < rad < 1 67 | Normalized radius of the hypersphere centered on the best particle. The 68 | higher the number of other particles inside and the better is the solution. 69 | args 70 | Tuple containing any parameter that needs to be passed to the function. If 71 | no parameters are passed set args=None. 72 | Xinit 73 | Initial position of each agent. 74 | nVar 75 | Number of variables. 76 | nRun 77 | Number of runs. 78 | random_pop = True, False 79 | Specifies if the number of agents (for each run) is kept constant at nPop 80 | or randomly choosen from the uniform distribution nVar/2 <= nPop <= nVar/2. 81 | Xsol 82 | Solution to the minimization point. Set Xsol=None if not known. 83 | 84 | Examples 85 | -------- 86 | There are four examples: Parabola, Alpine, Tripod, and Ackley. 87 | 88 | - Parabola, Alpine, and Ackley can have an arbitrary number of dimensions, 89 | while Tripod has only two dimensions. 90 | 91 | - Parabola, Tripod, and Ackley are examples where parameters (respectively, 92 | array X0, scalars kx and ky, and array X0) are passed using args. 93 | 94 | - The global minimum for Parabola and Ackley is at X0; the global minimum for 95 | Alpine is at zero; the global minimum for Tripod is at [0,-ky] with local 96 | minimum at [-kx,+ky] and [+kx,+ky]. 97 | """ 98 | 99 | import sys 100 | import numpy as np 101 | from pso import PSO 102 | 103 | # Read example to run 104 | if len(sys.argv) != 2: 105 | print("Usage: python test.py ") 106 | sys.exit(1) 107 | example = sys.argv[1] 108 | 109 | # Parabola: F(X) = sum((X - X0)^2), Xmin = X0 110 | if (example == 'Parabola'): 111 | 112 | def Parabola(X, args): 113 | X0 = args 114 | F = ((X - X0) ** 2).sum(axis=1) 115 | return F 116 | 117 | # Problem parameters 118 | nVar = 30 119 | nRun = 15 120 | random_pop = False 121 | X0 = np.ones(nVar) * 1.2345 # Args 122 | 123 | # PSO parameters 124 | func = Parabola 125 | UB = np.ones(nVar) * 20.0 126 | LB = - UB 127 | nPop = 40 128 | epochs = 500 129 | K = 3 130 | phi = 2.05 131 | vel_fact = 0.5 132 | conf_type = 'RB' 133 | IntVar = None 134 | normalize = False 135 | rad = 0.1 136 | args = (X0) 137 | Xinit = None 138 | 139 | # Solution 140 | Xsol = X0 141 | 142 | # Alpine: F(X) = sum(abs(X*sin(X) + 0.1*X)), Xmin = 0 143 | elif (example == 'Alpine'): 144 | 145 | def Alpine(X, args): 146 | F = np.abs(X * np.sin(X) + 0.1 * X).sum(axis=1) 147 | return F 148 | 149 | # Function parameters 150 | nVar = 10 151 | nRun = 15 152 | random_pop = False 153 | 154 | # PSO parameters 155 | func = Alpine 156 | UB = np.ones(nVar) * 10.0 157 | LB = - UB 158 | nPop = 100 159 | epochs = 500 160 | K = 3 161 | phi = 2.05 162 | vel_fact = 0.5 163 | conf_type = 'RB' 164 | IntVar = None 165 | normalize = False 166 | rad = 0.1 167 | args = None 168 | Xinit = None 169 | 170 | # Solution 171 | Xsol = np.zeros(nVar) 172 | 173 | # Tripod: 174 | # F(x,y)= p(y)*(1 + p(x)) + abs(x + kx*p(y)*(1 - 2*p(x))) 175 | # + abs(y + ky*(1 - 2*p(y))) 176 | # p(x) = 1 if x >= 0, p(x) = 0 if x < 0; p(y) = 1 if y >= 0, p(y) = 0 if y < 0 177 | # Global minimum at [0,-ky], local minimum at [-kx,ky] and [kx,ky]; kx, ky > 0 178 | elif (example == 'Tripod'): 179 | 180 | def Tripod(X, args): 181 | x = X[:, 0] 182 | y = X[:, 1] 183 | kx = args[0] 184 | ky = args[1] 185 | px = (x >= 0.0) 186 | py = (y >= 0.0) 187 | F = py * (1.0 + px) + np.abs(x + kx * py * (1.0 - 2.0 * px)) \ 188 | + np.abs(y + ky * (1.0 - 2.0 * py)) 189 | return F 190 | 191 | # Function parameters 192 | nVar = 2 # The equation works only with two dimensions 193 | nRun = 15 194 | random_pop = False 195 | kx = 20.0 # Args 196 | ky = 40.0 197 | 198 | # PSO parameters 199 | func = Tripod 200 | UB = np.ones(nVar) * 100.0 201 | LB = - UB 202 | nPop = 100 203 | epochs = 500 204 | K = 3 205 | phi = 2.05 206 | vel_fact = 0.5 207 | conf_type = 'RB' 208 | IntVar = None 209 | normalize = False 210 | rad = 0.1 211 | args = (kx, ky) 212 | Xinit = None 213 | 214 | # Solution 215 | Xsol = np.array([0.0, -ky]) 216 | 217 | # Ackley: F(X)= 20 + exp(1) - exp(sum(cos(2*pi*X))/n) 218 | # - 20*exp(-0.2*sqrt(sum(X^2)/n)) 219 | # Xmin = X0 220 | elif (example == 'Ackley'): 221 | 222 | def Ackley(X, args): 223 | X0 = args 224 | n = float(X.shape[1]) 225 | F = 20.0 + np.exp(1.0) \ 226 | - np.exp((np.cos(2.0 * np.pi * (X - X0))).sum(axis=1) / n) \ 227 | - 20.0 * np.exp(-0.2 * np.sqrt(((X - X0) ** 2).sum(axis=1) / n)) 228 | return F 229 | 230 | # Function parameters 231 | nVar = 30 232 | nRun = 15 233 | random_pop = False 234 | X0 = np.ones(nVar) * 1.6 # args 235 | 236 | # PSO parameters 237 | func = Ackley 238 | UB = np.ones(nVar) * 30.0 239 | LB = - UB 240 | nPop = 100 241 | epochs = 500 242 | K = 3 243 | phi = 2.05 244 | vel_fact = 0.5 245 | conf_type = 'RB' 246 | IntVar = None 247 | normalize = True 248 | rad = 0.1 249 | args = (X0) 250 | Xinit = None 251 | 252 | # Solution 253 | Xsol = X0 254 | 255 | else: 256 | print("Function not found") 257 | sys.exit(1) 258 | 259 | # ======= Main Code ======= # 260 | 261 | np.random.seed(1294404794) # Seed random generator 262 | 263 | # Define number of agents for each run 264 | if (random_pop): 265 | delta = nVar // 2 266 | agents = np.random.randint(nPop - delta, nPop + delta, size=nRun) 267 | else: 268 | agents = np.ones(nRun, dtype=int) * nPop 269 | 270 | print() 271 | print("Function: ", example) 272 | 273 | best_pos = np.zeros((nRun, nVar)) 274 | best_cost = np.zeros(nRun) 275 | 276 | # Run cases 277 | for run in range(nRun): 278 | 279 | # Optimize this run 280 | nPop = agents[run] 281 | X, info = PSO(func, LB, UB, nPop=nPop, epochs=epochs, K=K, phi=phi, 282 | vel_fact=vel_fact, conf_type=conf_type, IntVar=IntVar, 283 | normalize=normalize, rad=rad, args=args, Xinit=Xinit) 284 | 285 | # Save best position/cost for each run 286 | best_pos[run, :] = X 287 | best_cost[run] = info[0] 288 | 289 | # Print run number, agents number, error, and number of particles in close 290 | # proximity 291 | if (Xsol is not None): 292 | print("run = {0:<3d} nPop = {1:<3d} Error: {2:e} Close = {3:<3d}" 293 | .format(run+1, nPop, np.linalg.norm(Xsol - X), info[2])) 294 | else: 295 | print("run = {0:<3d} nPop = {1:<3d}".format(run+1, nPop)) 296 | 297 | # Results 298 | best_pos_mean = best_pos.mean(axis=0) 299 | best_pos_std = best_pos.std(axis=0) 300 | print() 301 | print("Best position:") 302 | for var in range(nVar): 303 | print("var = {0:<3d} mean = {1: e} std = {2:e}" 304 | .format(var+1, best_pos_mean[var], best_pos_std[var])) 305 | print() 306 | print("Best cost: mean = {0: e} std = {1:e}" 307 | .format(best_cost.mean(), best_cost.std())) 308 | print() 309 | if (Xsol is not None): 310 | error = np.linalg.norm(Xsol - best_pos_mean) 311 | print("Error: {0:e}".format(error)) 312 | -------------------------------------------------------------------------------- /Code_Python/pso.py: -------------------------------------------------------------------------------- 1 | """ 2 | Metaheuristic Minimization Using Particle Swarm Optimization. 3 | 4 | Copyright (c) 2021 Gabriele Gilardi 5 | """ 6 | 7 | import numpy as np 8 | 9 | 10 | def PSO(func, LB, UB, nPop=40, epochs=500, K=0, phi=2.05, vel_fact=0.5, 11 | conf_type='RB', IntVar=None, normalize=False, rad=0.1, args=None, 12 | Xinit=None): 13 | """ 14 | func Function to minimize 15 | LB Lower boundaries of the search space 16 | UB Upper boundaries of the search space 17 | nPop Number of agents (population) 18 | epochs Number of iterations 19 | K Average size of each agent's group of informants 20 | phi Coefficient to calculate the two confidence coefficients 21 | vel_fact Velocity factor to calculate the maximum and the minimum 22 | allowed velocities 23 | conf_type Confinement type (on the velocities) 24 | IntVar List of indexes specifying which variable should be treated 25 | as integers 26 | normalize Specifies if the search space should be normalized (to 27 | improve convergency) 28 | rad Normalized radius of the hypersphere centered on the best 29 | particle 30 | args Tuple containing any parameter that needs to be passed to 31 | the function 32 | Xinit Initial position of each agent 33 | 34 | Dimensions: 35 | (nVar, ) LB, UB, LB_orig, UB_orig, vel_max, vel_min, swarm_best_pos 36 | Xinit 37 | (nPop, nVar) agent_pos, agent_vel, agent_best_pos, Gr, group_best_pos, 38 | agent_pos_orig, agent_pos_tmp, vel_conf, out, x_sphere, u 39 | (nPop, nPop) informants, informants_cost 40 | (nPop) agent_best_cost, agent_cost, p_equal_g, better, r_max, r, 41 | norm 42 | (0-nVar, ) IntVar 43 | """ 44 | # Dimension of the search space and max. allowed velocities 45 | nVar = len(LB) 46 | vel_max = vel_fact * (UB - LB) 47 | vel_min = - vel_max 48 | 49 | # Confidence coefficients 50 | w = 1.0 / (phi - 1.0 + np.sqrt(phi**2 - 2.0 * phi)) 51 | cmax = w * phi 52 | 53 | # Probability an agent is an informant 54 | p_informant = 1.0 - (1.0 - 1.0 / float(nPop)) ** K 55 | 56 | # Normalize search space 57 | if (normalize): 58 | LB_orig = LB.copy() 59 | UB_orig = UB.copy() 60 | LB = np.zeros(nVar) 61 | UB = np.ones(nVar) 62 | 63 | # Define (if any) which variables are treated as integers (indexes are in 64 | # the range 1 to nVar) 65 | if (IntVar is None): 66 | nIntVar = 0 67 | elif (IntVar == 'all'): 68 | IntVar = np.arange(nVar, dtype=int) 69 | nIntVar = nVar 70 | else: 71 | IntVar = np.asarray(IntVar, dtype=int) - 1 72 | nIntVar = len(IntVar) 73 | 74 | # Initial position of each agent 75 | if (Xinit is None): 76 | agent_pos = LB + np.random.rand(nPop, nVar) * (UB - LB) 77 | else: 78 | Xinit = np.tile(Xinit, (nPop, 1)) 79 | if (normalize): 80 | agent_pos = (Xinit - LB_orig) / (UB_orig - LB_orig) 81 | else: 82 | agent_pos = Xinit 83 | 84 | if (nIntVar > 0): 85 | agent_pos[:, IntVar] = np.round(agent_pos[:, IntVar]) 86 | 87 | # Initial velocity of each agent (with velocity limits) 88 | agent_vel = (LB - agent_pos) + np.random.rand(nPop, nVar) * (UB - LB) 89 | agent_vel = np.fmin(np.fmax(agent_vel, vel_min), vel_max) 90 | 91 | # Initial cost of each agent 92 | if (normalize): 93 | agent_pos_orig = LB_orig + agent_pos * (UB_orig - LB_orig) 94 | agent_cost = func(agent_pos_orig, args) 95 | else: 96 | agent_cost = func(agent_pos, args) 97 | 98 | # Initial best position/cost of each agent 99 | agent_best_pos = agent_pos.copy() 100 | agent_best_cost = agent_cost.copy() 101 | 102 | # Initial best position/cost of the swarm 103 | idx = np.argmin(agent_best_cost) 104 | swarm_best_pos = agent_best_pos[idx, :] 105 | swarm_best_cost = agent_best_cost[idx] 106 | swarm_best_idx = idx 107 | 108 | # Initial best position of each agent using the swarm 109 | if (K == 0): 110 | group_best_pos = np.tile(swarm_best_pos, (nPop, 1)) 111 | p_equal_g = \ 112 | (np.where(np.arange(nPop) == idx, 0.75, 1.0)).reshape(nPop, 1) 113 | 114 | # Initial best position of each agent using informants 115 | else: 116 | informants = np.where(np.random.rand(nPop, nPop) < p_informant, 1, 0) 117 | np.fill_diagonal(informants, 1) 118 | group_best_pos, p_equal_g = group_best(informants, agent_best_pos, 119 | agent_best_cost) 120 | 121 | # Main loop 122 | for epoch in range(epochs): 123 | 124 | # Determine the updated velocity for each agent 125 | Gr = agent_pos + (1.0 / 3.0) * cmax * \ 126 | (agent_best_pos + group_best_pos - 2.0 * agent_pos) * p_equal_g 127 | x_sphere = hypersphere_point(Gr, agent_pos) 128 | agent_vel = w * agent_vel + Gr + x_sphere - agent_pos 129 | 130 | # Impose velocity limits 131 | agent_vel = np.fmin(np.fmax(agent_vel, vel_min), vel_max) 132 | 133 | # Temporarly update the position of each agent to check if it is 134 | # outside the search space 135 | agent_pos_tmp = agent_pos + agent_vel 136 | if (nIntVar > 0): 137 | agent_pos_tmp[:, IntVar] = np.round(agent_pos_tmp[:, IntVar]) 138 | out = np.logical_not((agent_pos_tmp > LB) * (agent_pos_tmp < UB)) 139 | 140 | # Apply velocity confinement rules 141 | if (conf_type == 'RB'): 142 | vel_conf = random_back_conf(agent_vel) 143 | 144 | elif (conf_type == 'HY'): 145 | vel_conf = hyperbolic_conf(agent_pos, agent_vel, UB, LB) 146 | 147 | elif (conf_type == 'MX'): 148 | vel_conf = mixed_conf(agent_pos, agent_vel, UB, LB) 149 | 150 | # Update velocity and position of each agent (all velocities 151 | # are smaller than the max. allowed velocity) 152 | agent_vel = np.where(out, vel_conf, agent_vel) 153 | agent_pos += agent_vel 154 | if (nIntVar > 0): 155 | agent_pos[:, IntVar] = np.round(agent_pos[:, IntVar]) 156 | 157 | # Apply position confinement rules to agents outside the search space 158 | agent_pos = np.fmin(np.fmax(agent_pos, LB), UB) 159 | if (nIntVar > 0): 160 | agent_pos[:, IntVar] = np.fmax(agent_pos[:, IntVar], 161 | np.ceil(LB[IntVar])) 162 | agent_pos[:, IntVar] = np.fmin(agent_pos[:, IntVar], 163 | np.floor(UB[IntVar])) 164 | 165 | # Calculate new cost of each agent 166 | if (normalize): 167 | agent_pos_orig = LB_orig + agent_pos * (UB_orig - LB_orig) 168 | agent_cost = func(agent_pos_orig, args) 169 | else: 170 | agent_cost = func(agent_pos, args) 171 | 172 | # Update best position/cost of each agent 173 | better = (agent_cost < agent_best_cost) 174 | agent_best_pos[better, :] = agent_pos[better, :] 175 | agent_best_cost[better] = agent_cost[better] 176 | 177 | # Update best position/cost of the swarm 178 | idx = np.argmin(agent_best_cost) 179 | if (agent_best_cost[idx] < swarm_best_cost): 180 | swarm_best_pos = agent_best_pos[idx, :] 181 | swarm_best_cost = agent_best_cost[idx] 182 | swarm_best_idx = idx 183 | 184 | # If the best cost of the swarm did not improve .... 185 | else: 186 | # .... when using swarm -> do nothing 187 | if (K == 0): 188 | pass 189 | 190 | # .... when using informants -> change informant groups 191 | else: 192 | informants = \ 193 | np.where(np.random.rand(nPop, nPop) < p_informant, 1, 0) 194 | np.fill_diagonal(informants, 1) 195 | 196 | # Update best position of each agent using the swarm 197 | if (K == 0): 198 | group_best_pos = np.tile(swarm_best_pos, (nPop, 1)) 199 | 200 | # Update best position of each agent using informants 201 | else: 202 | group_best_pos, p_equal_g, = group_best(informants, agent_best_pos, 203 | agent_best_cost) 204 | 205 | # If necessary de-normalize and determine the (normalized) distance between 206 | # the best particle and all the others 207 | if (normalize): 208 | delta = agent_best_pos - swarm_best_pos # (UB-LB = 1) 209 | swarm_best_pos = LB_orig + swarm_best_pos * (UB_orig - LB_orig) 210 | else: 211 | deltaB = np.fmax(UB-LB, 1.e-10) # To avoid /0 when LB = UB 212 | delta = (agent_best_pos - swarm_best_pos) / deltaB 213 | 214 | # Number of particles in the hypersphere of radius around the best 215 | # particle 216 | dist = np.linalg.norm(delta/np.sqrt(nPop), axis=1) 217 | in_rad = (dist < rad).sum() 218 | 219 | # Return info about the solution 220 | info = (swarm_best_cost, swarm_best_idx, in_rad) 221 | 222 | return swarm_best_pos, info 223 | 224 | 225 | def group_best(informants, agent_best_pos, agent_best_cost): 226 | """ 227 | Determines the group best position of each agent based on the agent 228 | informants. 229 | """ 230 | nPop, nVar = agent_best_pos.shape 231 | 232 | # Determine the cost of each agent in each group (set to infinity the value 233 | # for agents that are not informants of the group) 234 | informants_cost = np.where(informants == 1, agent_best_cost, np.inf) 235 | 236 | # For each agent determine the agent with the best cost in the group and 237 | # assign its position to it 238 | idx = np.argmin(informants_cost, axis=1) 239 | group_best_pos = agent_best_pos[idx, :] 240 | 241 | # Build the vector to correct the velocity update for the corner case where 242 | # the agent is also the group best 243 | p_equal_g = (np.where(np.arange(nPop) == idx, 0.75, 1.0)).reshape(nPop, 1) 244 | 245 | return group_best_pos, p_equal_g 246 | 247 | 248 | def hypersphere_point(Gr, agent_pos): 249 | """ 250 | For each agent determines a random point inside the hypersphere (Gr,|Gr-X|), 251 | where Gr is its center, |Gr-X| is its radius, and X is the agent position. 252 | """ 253 | nPop, nVar = agent_pos.shape 254 | 255 | # Hypersphere radius of each agent 256 | r_max = np.linalg.norm(Gr - agent_pos, axis=1) 257 | 258 | # Randomly pick a direction using a normal distribution and a radius 259 | # (inside the hypersphere) 260 | u = np.random.normal(0.0, 1.0, (nPop, nVar)) 261 | norm = np.linalg.norm(u, axis=1) 262 | r = np.random.uniform(0.0, r_max, nPop) 263 | 264 | # Coordinates of the point with direction and at distance from the 265 | # hypersphere center 266 | x_sphere = u * (r / norm).reshape(nPop, 1) 267 | 268 | return x_sphere 269 | 270 | 271 | def hyperbolic_conf(agent_pos, agent_vel, UB, LB): 272 | """ 273 | Applies hyperbolic confinement to agent velocities (calculation is done on 274 | all agents to avoid loops but the change will be applied only to the agents 275 | actually outside the search space). 276 | """ 277 | # If the update velocity is > 0 278 | if_pos_vel = agent_vel / (1.0 + np.abs(agent_vel / (UB - agent_pos))) 279 | 280 | # If the update velocity is <= 0 281 | if_neg_vel = agent_vel / (1.0 + np.abs(agent_vel / (agent_pos - LB))) 282 | 283 | # Confinement velocity 284 | vel_conf = np.where(agent_vel > 0, if_pos_vel, if_neg_vel) 285 | 286 | return vel_conf 287 | 288 | 289 | def random_back_conf(agent_vel): 290 | """ 291 | Applies random-back confinement to agent velocities (calculation is done on 292 | all agents to avoid loops but the change will be applied only to the agents 293 | actually outside the search space). 294 | """ 295 | nPop, nVar = agent_vel.shape 296 | 297 | # Confinement velocity 298 | vel_conf = - np.random.rand(nPop, nVar) * agent_vel 299 | 300 | return vel_conf 301 | 302 | 303 | def mixed_conf(agent_pos, agent_vel, UB, LB): 304 | """ 305 | Applies a mixed-type confinement to agent velocities (calculation is done on 306 | all agents to avoid loops but the change will be applied only to the agents 307 | actually outside the search space). 308 | 309 | For each agent the confinement type (hyperbolic or random-back) is choosen 310 | randomly. 311 | """ 312 | nPop, nVar = agent_pos.shape 313 | 314 | # Hyperbolic confinement 315 | vel_conf_HY = hyperbolic_conf(agent_pos, agent_vel, UB, LB) 316 | 317 | # random-back confinement 318 | vel_conf_RB = random_back_conf(agent_vel) 319 | 320 | # Confinement velocity 321 | gamma = np.random.rand(nPop, nVar) 322 | vel_conf = np.where(gamma >= 0.5, vel_conf_HY, vel_conf_RB) 323 | 324 | return vel_conf 325 | --------------------------------------------------------------------------------