Source code for RCAIDE.Framework.Optimization.Packages.particle_swarm.particle_swarm_optimization

# particle_swarm_optimization.py
# 
# Created:  Sep. 2019, M. Clarke

# ----------------------------------------------------------------------
#  Imports
# ----------------------------------------------------------------------

# RCAIDE imports
import numpy as np
import scipy as sp
  
[docs] def particle_swarm_optimization(func, lb, ub, ieqcons=[], f_ieqcons=None, args=(), kwargs={}, swarmsize=100, omega=0.5, phip=0.5, phig=0.5, maxiter=100, minstep=1e-8, minfunc=1e-8, debug=False): """ This function perform a particle swarm optimization (PSO) Source: Pyswarm: https://github.com/tisimst/pyswarm Inputs: func : The function to be minimized [function] lb : The lower bounds of the design variable(s) [array] ub : The upper bounds of the design variable(s) [array] ieqcons : A list of functions of length n such that ieqcons[j](x,*args) >= 0.0 in a successfully optimized problem (Default: []) [list] f_ieqcons : Returns a 1-D array in which each element must be greater or equal to 0.0 in a successfully optimized problem. If f_ieqcons is specified, ieqcons is ignored (Default: None) [function] args : Additional arguments passed to objective and constraint functions [tuple] kwargs : Additional keyword arguments passed to objective and constraint functions [dict] swarmsize : The number of particles in the swarm (Default: 100) [int] omega : Particle velocity scaling factor (Default: 0.5) [float] phip : Scaling factor to search away from the particle's best known position (Default: 0.5) [scalar] phig : Scaling factor to search away from the swarm's best known position (Default: 0.5) [scalar] maxiter : The maximum number of iterations for the swarm to search (Default: 100) [int] minstep : The minimum stepsize of swarm's best position before the search terminates (Default: 1e-8) [scalar] minfunc : The minimum change of swarm's best objective value before the search terminates (Default: 1e-8) [scalar] debug : If True, progress statements will be displayed every iteration (Default: False) [boolean] Outputs: g : The swarm's best known position (optimal design) [list] f : The objective value at ``g`` [float] Properties Used: None """ # License ''' Copyright (c) 2015, Sebastian M. Castillo Hair All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by Sebastian M. Castillo Hair. 4. Neither the name of Sebastian M. Castillo Hair nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY SEBASTIAN M. CASTILLO HAIR ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEBASTIAN M. CASTILLO HAIR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''' assert len(lb)==len(ub), 'Lower- and upper-bounds must be the same length' assert hasattr(func, '__call__'), 'Invalid function handle' lb = np.array(lb) ub = np.array(ub) assert np.all(ub>lb), 'All upper-bound values must be greater than lower-bound values' vhigh = np.abs(ub - lb) vlow = -vhigh # Check for constraint function(s) ######################################### obj = lambda x: func(x, *args, **kwargs) if f_ieqcons is None: if not len(ieqcons): if debug: print('No constraints given.') cons = lambda x: np.array([0]) else: if debug: print('Converting ieqcons to a single constraint function') cons = lambda x: np.array([y(x, *args, **kwargs) for y in ieqcons]) else: if debug: print('Single constraint function given in f_ieqcons') cons = lambda x: np.array(f_ieqcons(x, *args, **kwargs)) def is_feasible(x): check = np.all(cons(x)>=0) return check # Initialize the particle swarm ############################################ S = swarmsize D = len(lb) # the number of dimensions each particle has x = np.random.rand(S, D) # particle positions v = np.zeros_like(x) # particle velocities p = np.zeros_like(x) # best particle positions fp = np.zeros(S) # best particle function values g = [] # best swarm position fg = 1e100 # artificial best swarm position starting value for i in range(S): # Initialize the particle's position x[i, :] = lb + x[i, :]*(ub - lb) # Initialize the particle's best known position p[i, :] = x[i, :] # Calculate the objective's value at the current particle's fp[i] = obj(p[i, :]) # At the start, there may not be any feasible starting point, so just # give it a temporary "best" point since it's likely to change if i==0: g = p[0, :].copy() # If the current particle's position is better than the swarm's, # update the best swarm position if fp[i]<fg and is_feasible(p[i, :]): fg = fp[i] g = p[i, :].copy() # Initialize the particle's velocity v[i, :] = vlow + np.random.rand(D)*(vhigh - vlow) # Iterate until termination criterion met ################################## it = 1 while it<=maxiter: rp = np.random.uniform(size=(S, D)) rg = np.random.uniform(size=(S, D)) for i in range(S): # Update the particle's velocity v[i, :] = omega*v[i, :] + phip*rp[i, :]*(p[i, :] - x[i, :]) + \ phig*rg[i, :]*(g - x[i, :]) # Update the particle's position, correcting lower and upper bound # violations, then update the objective function value x[i, :] = x[i, :] + v[i, :] mark1 = x[i, :]<lb mark2 = x[i, :]>ub x[i, mark1] = lb[mark1] x[i, mark2] = ub[mark2] fx = obj(x[i, :]) # Compare particle's best position (if constraints are satisfied) if fx<fp[i] and is_feasible(x[i, :]): p[i, :] = x[i, :].copy() fp[i] = fx # Compare swarm's best position to current particle's position # (Can only get here if constraints are satisfied) if fx<fg: if debug: print('New best for swarm at iteration {:}: {:} {:}'.format(it, x[i, :], fx)) tmp = x[i, :].copy() stepsize = np.sqrt(np.sum((g-tmp)**2)) if np.abs(fg - fx)<=minfunc: print('Stopping search: Swarm best objective change less than {:}'.format(minfunc)) return tmp, fx elif stepsize<=minstep: print('Stopping search: Swarm best position change less than {:}'.format(minstep)) return tmp, fx else: g = tmp.copy() fg = fx if debug: print('Best after iteration {:}: {:} {:}'.format(it, g, fg)) it += 1 print('Stopping search: maximum iterations reached --> {:}'.format(maxiter)) if not is_feasible(g): print("However, the optimization couldn't find a feasible design. Sorry") return g, fg